messageformat2 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- messageformat2-0.1.0/.github/workflows/ci.yml +34 -0
- messageformat2-0.1.0/.gitignore +10 -0
- messageformat2-0.1.0/.readthedocs.yaml +19 -0
- messageformat2-0.1.0/CONTRIBUTING.md +29 -0
- messageformat2-0.1.0/LICENSE +21 -0
- messageformat2-0.1.0/PKG-INFO +148 -0
- messageformat2-0.1.0/README.md +89 -0
- messageformat2-0.1.0/docs/builtins.md +172 -0
- messageformat2-0.1.0/docs/datamodel.md +10 -0
- messageformat2-0.1.0/docs/errors.md +4 -0
- messageformat2-0.1.0/docs/explanation.md +86 -0
- messageformat2-0.1.0/docs/getting_started.md +58 -0
- messageformat2-0.1.0/docs/howto/datamodel.md +60 -0
- messageformat2-0.1.0/docs/howto/errors.md +48 -0
- messageformat2-0.1.0/docs/howto/formatter.md +51 -0
- messageformat2-0.1.0/docs/howto/selector.md +66 -0
- messageformat2-0.1.0/docs/index.md +37 -0
- messageformat2-0.1.0/docs/message.md +9 -0
- messageformat2-0.1.0/docs/mf2.md +13 -0
- messageformat2-0.1.0/docs/reference.md +12 -0
- messageformat2-0.1.0/messageformat2/__init__.py +5 -0
- messageformat2-0.1.0/messageformat2/builtins.py +323 -0
- messageformat2-0.1.0/messageformat2/cli.py +51 -0
- messageformat2-0.1.0/messageformat2/datamodel.py +507 -0
- messageformat2-0.1.0/messageformat2/errors.py +151 -0
- messageformat2-0.1.0/messageformat2/message.py +127 -0
- messageformat2-0.1.0/messageformat2/parser.py +557 -0
- messageformat2-0.1.0/messageformat2/runtime.py +301 -0
- messageformat2-0.1.0/messageformat2.egg-info/PKG-INFO +148 -0
- messageformat2-0.1.0/messageformat2.egg-info/SOURCES.txt +70 -0
- messageformat2-0.1.0/messageformat2.egg-info/dependency_links.txt +1 -0
- messageformat2-0.1.0/messageformat2.egg-info/entry_points.txt +2 -0
- messageformat2-0.1.0/messageformat2.egg-info/requires.txt +20 -0
- messageformat2-0.1.0/messageformat2.egg-info/top_level.txt +1 -0
- messageformat2-0.1.0/mkdocs.yml +50 -0
- messageformat2-0.1.0/pyproject.toml +92 -0
- messageformat2-0.1.0/requirements.dev.txt +176 -0
- messageformat2-0.1.0/requirements.docs.txt +96 -0
- messageformat2-0.1.0/requirements.txt +4 -0
- messageformat2-0.1.0/setup.cfg +4 -0
- messageformat2-0.1.0/tests/__init__.py +0 -0
- messageformat2-0.1.0/tests/data/complex_message_1.txt +31 -0
- messageformat2-0.1.0/tests/data/complex_message_2.txt +27 -0
- messageformat2-0.1.0/tests/data/complex_message_3.txt +22 -0
- messageformat2-0.1.0/tests/data/simple_message_1.txt +1 -0
- messageformat2-0.1.0/tests/data/simple_message_10.txt +10 -0
- messageformat2-0.1.0/tests/data/simple_message_11.txt +12 -0
- messageformat2-0.1.0/tests/data/simple_message_12.txt +10 -0
- messageformat2-0.1.0/tests/data/simple_message_13.txt +8 -0
- messageformat2-0.1.0/tests/data/simple_message_14.txt +10 -0
- messageformat2-0.1.0/tests/data/simple_message_15.txt +15 -0
- messageformat2-0.1.0/tests/data/simple_message_16.txt +10 -0
- messageformat2-0.1.0/tests/data/simple_message_2.txt +1 -0
- messageformat2-0.1.0/tests/data/simple_message_3.txt +3 -0
- messageformat2-0.1.0/tests/data/simple_message_4.txt +4 -0
- messageformat2-0.1.0/tests/data/simple_message_5.txt +7 -0
- messageformat2-0.1.0/tests/data/simple_message_6.txt +11 -0
- messageformat2-0.1.0/tests/data/simple_message_7.txt +18 -0
- messageformat2-0.1.0/tests/data/simple_message_8.txt +12 -0
- messageformat2-0.1.0/tests/data/simple_message_9.txt +15 -0
- messageformat2-0.1.0/tests/data/simple_message_escapes_1.txt +1 -0
- messageformat2-0.1.0/tests/data/simple_message_escapes_2.txt +1 -0
- messageformat2-0.1.0/tests/data/unsupported_annotations_1.txt +4 -0
- messageformat2-0.1.0/tests/data/unsupported_annotations_2.txt +4 -0
- messageformat2-0.1.0/tests/data/unsupported_annotations_3.txt +3 -0
- messageformat2-0.1.0/tests/data/unsupported_annotations_4.txt +6 -0
- messageformat2-0.1.0/tests/data/unsupported_annotations_5.txt +8 -0
- messageformat2-0.1.0/tests/data/unsupported_statement_1.txt +15 -0
- messageformat2-0.1.0/tests/data/unsupported_statement_2.txt +21 -0
- messageformat2-0.1.0/tests/test_parser.py +235 -0
- messageformat2-0.1.0/tests/test_regex.py +210 -0
- messageformat2-0.1.0/tests/test_runtime.py +242 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ci:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Setup Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install MF2
|
|
25
|
+
run: pip install .[dev]
|
|
26
|
+
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: ruff check tests/ messageformat2/ --exclude messageformat2/cli.py
|
|
29
|
+
|
|
30
|
+
- name: Test
|
|
31
|
+
run: pytest --doctest-modules
|
|
32
|
+
|
|
33
|
+
- name: Static type checking
|
|
34
|
+
uses: jakebailey/pyright-action@v2
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Read the Docs configuration file for MkDocs projects
|
|
2
|
+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
3
|
+
|
|
4
|
+
# Required
|
|
5
|
+
version: 2
|
|
6
|
+
|
|
7
|
+
# Set the version of Python and other tools you might need
|
|
8
|
+
build:
|
|
9
|
+
os: ubuntu-22.04
|
|
10
|
+
tools:
|
|
11
|
+
python: "3.12"
|
|
12
|
+
|
|
13
|
+
mkdocs:
|
|
14
|
+
configuration: mkdocs.yml
|
|
15
|
+
|
|
16
|
+
# Optionally declare the Python requirements required to build your docs
|
|
17
|
+
python:
|
|
18
|
+
install:
|
|
19
|
+
- requirements: requirements.docs.txt
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Contributing to MessageFormat2
|
|
2
|
+
|
|
3
|
+
You can help improve this project by either opening issues or submitting pull requests for
|
|
4
|
+
|
|
5
|
+
- bugs
|
|
6
|
+
- differing behaviour between the package and [official specification](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html)
|
|
7
|
+
- documentation
|
|
8
|
+
- implementing missing features (e.g. missing options for formatters)
|
|
9
|
+
|
|
10
|
+
## Dev setup
|
|
11
|
+
|
|
12
|
+
Clone the repository and then run this command to install the package in editable mode:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pip install -e .[dev]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Running tests
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pytest --doctest-modules
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Code quality
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
pyright tests/ messageformat2/
|
|
28
|
+
ruff check tests/ messageformat2/
|
|
29
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) European Organization for Nuclear Research (CERN)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: messageformat2
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python implementation of Unicode MessageFormat 2.0
|
|
5
|
+
Author-email: Tomas Roun <tomas.roun8@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) European Organization for Nuclear Research (CERN)
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/tomasr8/messageformat2
|
|
29
|
+
Project-URL: Github, https://github.com/tomasr8/messageformat2
|
|
30
|
+
Keywords: i18n,internationalization,localization,messageformat,messageformat 2,cldr,gettext
|
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
32
|
+
Classifier: Operating System :: OS Independent
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
35
|
+
Classifier: Topic :: Scientific/Engineering
|
|
36
|
+
Classifier: Topic :: Software Development :: Internationalization
|
|
37
|
+
Classifier: Topic :: Software Development :: Localization
|
|
38
|
+
Requires-Python: >=3.12
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
License-File: LICENSE
|
|
41
|
+
Requires-Dist: Babel
|
|
42
|
+
Requires-Dist: click
|
|
43
|
+
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: build; extra == "dev"
|
|
45
|
+
Requires-Dist: twine; extra == "dev"
|
|
46
|
+
Requires-Dist: uv; extra == "dev"
|
|
47
|
+
Requires-Dist: pytest; extra == "dev"
|
|
48
|
+
Requires-Dist: pytest-snapshot; extra == "dev"
|
|
49
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
50
|
+
Requires-Dist: ruff; extra == "dev"
|
|
51
|
+
Requires-Dist: pyright; extra == "dev"
|
|
52
|
+
Requires-Dist: mkdocs; extra == "dev"
|
|
53
|
+
Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
54
|
+
Requires-Dist: mkdocs-material; extra == "dev"
|
|
55
|
+
Provides-Extra: docs
|
|
56
|
+
Requires-Dist: mkdocs; extra == "docs"
|
|
57
|
+
Requires-Dist: mkdocstrings[python]; extra == "docs"
|
|
58
|
+
Requires-Dist: mkdocs-material; extra == "docs"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# MessageFormat2 [](https://messageformat2.readthedocs.io/en/latest/?badge=latest) [](https://github.com/tomasr8/messageformat2/workflows/CI/badge.svg) [](https://microsoft.github.io/pyright/)
|
|
63
|
+
|
|
64
|
+
This is a Python implementation of the [Unicode Message Format 2.0
|
|
65
|
+
specification](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html).
|
|
66
|
+
|
|
67
|
+
Message Format 2.0 (MF2) is a new iteration of Message Format which aims to be
|
|
68
|
+
more expressive and flexible than both Message Format 1.0 and, in the context of
|
|
69
|
+
Python, gettext.
|
|
70
|
+
|
|
71
|
+
If you would like to learn more about MF2 itself head over to the [Unicode technical
|
|
72
|
+
specification](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html).
|
|
73
|
+
|
|
74
|
+
## Features
|
|
75
|
+
|
|
76
|
+
The library includes:
|
|
77
|
+
|
|
78
|
+
- A message parser and a formatter
|
|
79
|
+
- Several builtin formatters and selectors described by the
|
|
80
|
+
[spec](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html#function-registry)
|
|
81
|
+
and the possibility to write custom formatters/selectors
|
|
82
|
+
- Helper classes to inspect and transform the message [data
|
|
83
|
+
model](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html#interchange-data-model)
|
|
84
|
+
|
|
85
|
+
## [Documentation](https://messageformat2.readthedocs.io/en/latest/)
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
Via pip for Python `>= 3.12`
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
pip install messageformat2
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Examples
|
|
96
|
+
|
|
97
|
+
### Simple messages
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from messageformat2 import format_message
|
|
101
|
+
|
|
102
|
+
format_message("Hello, {$name}!", {"name": "Alice"}) # -> "Hello, Alice!"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Messages can be reused with different inputs.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from messageformat2 import Message
|
|
109
|
+
|
|
110
|
+
message = Message("Hello, {$name}!")
|
|
111
|
+
message.format({"name": "Alice"}) # -> "Hello, Alice!"
|
|
112
|
+
message.format({"name": "Bob"}) # -> "Hello, Bob!"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Built-in formatters
|
|
116
|
+
|
|
117
|
+
There are several builtin locale-aware formatters available.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from datetime import datetime
|
|
121
|
+
from messageformat2 import Message
|
|
122
|
+
|
|
123
|
+
message = Message("Today's date is {$now :date}")
|
|
124
|
+
now = datetime.now()
|
|
125
|
+
message.format({"now": now}, locale='en_US')
|
|
126
|
+
# -> Today's date is 5/10/2024
|
|
127
|
+
message.format({"now": now}, locale='en_GB')
|
|
128
|
+
# -> Today's date is 10/05/2024
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Plural support
|
|
132
|
+
|
|
133
|
+
MF2 supports pluralization using built-in or custom selectors.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from messageformat2 import Message
|
|
137
|
+
|
|
138
|
+
msg = Message ("""\
|
|
139
|
+
.match {$count :number}
|
|
140
|
+
one {{You have {$count} notification.}}
|
|
141
|
+
* {{You have {$count} notifications.}}
|
|
142
|
+
""")
|
|
143
|
+
|
|
144
|
+
msg.format({"count": 42}, locale='en_US') # -> "You have 42 notifications."
|
|
145
|
+
msg.format({"count": 1}, locale='en_US') # -> "You have 1 notification."
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Complete documentation is available [here](https://messageformat2.readthedocs.io/en/latest/).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
# MessageFormat2 [](https://messageformat2.readthedocs.io/en/latest/?badge=latest) [](https://github.com/tomasr8/messageformat2/workflows/CI/badge.svg) [](https://microsoft.github.io/pyright/)
|
|
4
|
+
|
|
5
|
+
This is a Python implementation of the [Unicode Message Format 2.0
|
|
6
|
+
specification](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html).
|
|
7
|
+
|
|
8
|
+
Message Format 2.0 (MF2) is a new iteration of Message Format which aims to be
|
|
9
|
+
more expressive and flexible than both Message Format 1.0 and, in the context of
|
|
10
|
+
Python, gettext.
|
|
11
|
+
|
|
12
|
+
If you would like to learn more about MF2 itself head over to the [Unicode technical
|
|
13
|
+
specification](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html).
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
The library includes:
|
|
18
|
+
|
|
19
|
+
- A message parser and a formatter
|
|
20
|
+
- Several builtin formatters and selectors described by the
|
|
21
|
+
[spec](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html#function-registry)
|
|
22
|
+
and the possibility to write custom formatters/selectors
|
|
23
|
+
- Helper classes to inspect and transform the message [data
|
|
24
|
+
model](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html#interchange-data-model)
|
|
25
|
+
|
|
26
|
+
## [Documentation](https://messageformat2.readthedocs.io/en/latest/)
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
Via pip for Python `>= 3.12`
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pip install messageformat2
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Simple messages
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from messageformat2 import format_message
|
|
42
|
+
|
|
43
|
+
format_message("Hello, {$name}!", {"name": "Alice"}) # -> "Hello, Alice!"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Messages can be reused with different inputs.
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from messageformat2 import Message
|
|
50
|
+
|
|
51
|
+
message = Message("Hello, {$name}!")
|
|
52
|
+
message.format({"name": "Alice"}) # -> "Hello, Alice!"
|
|
53
|
+
message.format({"name": "Bob"}) # -> "Hello, Bob!"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Built-in formatters
|
|
57
|
+
|
|
58
|
+
There are several builtin locale-aware formatters available.
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from datetime import datetime
|
|
62
|
+
from messageformat2 import Message
|
|
63
|
+
|
|
64
|
+
message = Message("Today's date is {$now :date}")
|
|
65
|
+
now = datetime.now()
|
|
66
|
+
message.format({"now": now}, locale='en_US')
|
|
67
|
+
# -> Today's date is 5/10/2024
|
|
68
|
+
message.format({"now": now}, locale='en_GB')
|
|
69
|
+
# -> Today's date is 10/05/2024
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Plural support
|
|
73
|
+
|
|
74
|
+
MF2 supports pluralization using built-in or custom selectors.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from messageformat2 import Message
|
|
78
|
+
|
|
79
|
+
msg = Message ("""\
|
|
80
|
+
.match {$count :number}
|
|
81
|
+
one {{You have {$count} notification.}}
|
|
82
|
+
* {{You have {$count} notifications.}}
|
|
83
|
+
""")
|
|
84
|
+
|
|
85
|
+
msg.format({"count": 42}, locale='en_US') # -> "You have 42 notifications."
|
|
86
|
+
msg.format({"count": 1}, locale='en_US') # -> "You have 1 notification."
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Complete documentation is available [here](https://messageformat2.readthedocs.io/en/latest/).
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
The default function registry includes these functions
|
|
2
|
+
|
|
3
|
+
in line with the specification in the CLDR 45 technical preview
|
|
4
|
+
|
|
5
|
+
### `:string`
|
|
6
|
+
|
|
7
|
+
#### Formatter
|
|
8
|
+
|
|
9
|
+
As a formatter, this functions simply call `str(value)`
|
|
10
|
+
|
|
11
|
+
#### Selector
|
|
12
|
+
|
|
13
|
+
As a selector, this function simply selects the exact match among the variant keys
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
format_message("""\
|
|
17
|
+
.match {$pet :string}
|
|
18
|
+
dog {{I have a dog}}
|
|
19
|
+
cat {{I have a cat}}
|
|
20
|
+
* {{I have a {$pet}}}
|
|
21
|
+
""", {"pet": "cat"})
|
|
22
|
+
# -> "I have a cat"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### `:number`
|
|
26
|
+
|
|
27
|
+
#### Formatter
|
|
28
|
+
|
|
29
|
+
- `compactDisplay`
|
|
30
|
+
- short (default)
|
|
31
|
+
- long
|
|
32
|
+
- notation
|
|
33
|
+
- standard
|
|
34
|
+
- scientific
|
|
35
|
+
- engineering (not implemented)
|
|
36
|
+
- compact
|
|
37
|
+
- `numberingSystem`
|
|
38
|
+
valid Unicode Number System Identifier (default is locale-specific)
|
|
39
|
+
- `signDisplay` (not implemented)
|
|
40
|
+
- `style`
|
|
41
|
+
- decimal (default)
|
|
42
|
+
- percent
|
|
43
|
+
- `useGrouping`
|
|
44
|
+
- auto (default)
|
|
45
|
+
- always
|
|
46
|
+
- min2
|
|
47
|
+
- `minimumFractionDigits` (not implemented)
|
|
48
|
+
- `maximumFractionDigits` (not implemented)
|
|
49
|
+
|
|
50
|
+
#### Selector
|
|
51
|
+
|
|
52
|
+
The selector takes the same options as the formatter in addition to:
|
|
53
|
+
|
|
54
|
+
- `select`
|
|
55
|
+
- plural (default)
|
|
56
|
+
- ordinal
|
|
57
|
+
- exact
|
|
58
|
+
|
|
59
|
+
The selector does exact string comparison with the variant keys but also keyword comparisons.
|
|
60
|
+
|
|
61
|
+
### `:integer`
|
|
62
|
+
|
|
63
|
+
#### Formatter
|
|
64
|
+
|
|
65
|
+
- `numberingSystem`
|
|
66
|
+
valid Unicode Number System Identifier (default is locale-specific)
|
|
67
|
+
- `signDisplay` (not implemented)
|
|
68
|
+
- `style`
|
|
69
|
+
- decimal (default)
|
|
70
|
+
- percent
|
|
71
|
+
- `useGrouping`
|
|
72
|
+
- auto (default)
|
|
73
|
+
- always
|
|
74
|
+
- min2
|
|
75
|
+
- `minimumIntegerDigits` (not implemented)
|
|
76
|
+
- `maximumSignificantDigits` (not implemented)
|
|
77
|
+
|
|
78
|
+
#### Selector
|
|
79
|
+
|
|
80
|
+
The selector takes the same options as the formatter in addition to:
|
|
81
|
+
|
|
82
|
+
- `select`
|
|
83
|
+
- plural (default)
|
|
84
|
+
- ordinal
|
|
85
|
+
- exact
|
|
86
|
+
|
|
87
|
+
The selector does exact string comparison with the variant keys but also keyword comparisons.
|
|
88
|
+
|
|
89
|
+
### `:datetime`
|
|
90
|
+
|
|
91
|
+
The value must either a `datetime` instance or an ISO8601 string that can be parsed using `datetime.fromisoformat`.
|
|
92
|
+
|
|
93
|
+
#### Formatter
|
|
94
|
+
|
|
95
|
+
- `Datestyle`
|
|
96
|
+
- full
|
|
97
|
+
- long
|
|
98
|
+
- medium
|
|
99
|
+
- short (default)
|
|
100
|
+
- `timeStyle` (not implemented)
|
|
101
|
+
- `weekday`:
|
|
102
|
+
- long
|
|
103
|
+
- short
|
|
104
|
+
- narrow
|
|
105
|
+
- `era`:
|
|
106
|
+
- long
|
|
107
|
+
- short
|
|
108
|
+
- narrow
|
|
109
|
+
- `year`:
|
|
110
|
+
- numeric
|
|
111
|
+
- 2-digit
|
|
112
|
+
- `month`:
|
|
113
|
+
- numeric
|
|
114
|
+
- 2-digit
|
|
115
|
+
- `day`:
|
|
116
|
+
- numeric
|
|
117
|
+
- 2-digit
|
|
118
|
+
- `hour`:
|
|
119
|
+
- numeric
|
|
120
|
+
- 2-digit
|
|
121
|
+
- `minute`:
|
|
122
|
+
- numeric
|
|
123
|
+
- 2-digit
|
|
124
|
+
- `second`:
|
|
125
|
+
- numeric
|
|
126
|
+
- 2-digit
|
|
127
|
+
- `fractionalSecondDigits`:
|
|
128
|
+
- 1
|
|
129
|
+
- 2
|
|
130
|
+
- 3
|
|
131
|
+
- `hourCycle`:
|
|
132
|
+
- h11
|
|
133
|
+
- h12
|
|
134
|
+
- h23
|
|
135
|
+
- h24
|
|
136
|
+
- `timeZoneName` (not implemented)
|
|
137
|
+
|
|
138
|
+
#### Selector
|
|
139
|
+
|
|
140
|
+
The `:date` selector is not supported currently as it is not required by MF2
|
|
141
|
+
|
|
142
|
+
### `:date`
|
|
143
|
+
|
|
144
|
+
The value must either a `datetime` instance or an ISO8601 string that can be parsed using `datetime.fromisoformat`.
|
|
145
|
+
|
|
146
|
+
#### Formatter
|
|
147
|
+
|
|
148
|
+
- `style`
|
|
149
|
+
- full
|
|
150
|
+
- long
|
|
151
|
+
- medium
|
|
152
|
+
- short (default)
|
|
153
|
+
|
|
154
|
+
#### Selector
|
|
155
|
+
|
|
156
|
+
The `:date` selector is not supported currently as it is not required by MF2
|
|
157
|
+
|
|
158
|
+
### `:time`
|
|
159
|
+
|
|
160
|
+
The value must either a `datetime.time` instance
|
|
161
|
+
|
|
162
|
+
#### Formatter
|
|
163
|
+
|
|
164
|
+
- `style`
|
|
165
|
+
- full
|
|
166
|
+
- long
|
|
167
|
+
- medium
|
|
168
|
+
- short (default)
|
|
169
|
+
|
|
170
|
+
#### Selector
|
|
171
|
+
|
|
172
|
+
The `:time` selector is not supported currently as it is not required by MF2
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
This part of the project documentation focuses on an
|
|
2
|
+
**understanding-oriented** approach. You'll get a
|
|
3
|
+
chance to read about the background of the project,
|
|
4
|
+
as well as reasoning about how it was implemented.
|
|
5
|
+
|
|
6
|
+
> **Note:** Expand this section by considering the
|
|
7
|
+
> following points:
|
|
8
|
+
|
|
9
|
+
- Give context and background on your library
|
|
10
|
+
- Explain why you created it
|
|
11
|
+
- Provide multiple examples and approaches of how
|
|
12
|
+
to work with it
|
|
13
|
+
- Help the reader make connections
|
|
14
|
+
- Avoid writing instructions or technical descriptions
|
|
15
|
+
here
|
|
16
|
+
|
|
17
|
+
Invalid syntax
|
|
18
|
+
A complex message cannot start with leading whitespace. Such a message is treated as a simple message by the grammar
|
|
19
|
+
|
|
20
|
+
## Whitespace
|
|
21
|
+
|
|
22
|
+
This library implements the [grammar](https://www.unicode.org/reports/tr35/tr35-72/tr35-messageFormat.html#complete-abnf) exactly as specified which includes whitespace.
|
|
23
|
+
According to the grammar, complex messages (messages containing declarations and/or the match block) cannot have a leading whitespace. For example, this message:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
.match {$count :integer}
|
|
27
|
+
* {{count is {$count}}}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
will be parsed as a simple message (message not containing any declarations/match blocks, only patterns `{...}`). The parsing will fail
|
|
31
|
+
because a pattern cannot contain unescaped `{`.
|
|
32
|
+
|
|
33
|
+
When defining complex messages using multiline strings, don't forget to use a leading `\` to remove the first newline character:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
Message("""\ # <- notice the backslash
|
|
37
|
+
.match {$count: integer}
|
|
38
|
+
* {{count is {$count}}}""")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
Message(""".match {$count: integer}
|
|
43
|
+
* {{count is {$count}}}""")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Computation model
|
|
47
|
+
|
|
48
|
+
All expressions are lazily evaluated at the time of use. If the same variable if referenced more than once, it will be evaaluated more than once.
|
|
49
|
+
IF you have formatters/selectors with side effects be careful.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
counter = 0
|
|
53
|
+
|
|
54
|
+
def formatter_with_side_effect(value, locale, options):
|
|
55
|
+
nonlocal counter
|
|
56
|
+
counter +=1
|
|
57
|
+
return str(value)
|
|
58
|
+
|
|
59
|
+
msg = Message("\
|
|
60
|
+
.local $foo = {|foo| :side_effect}
|
|
61
|
+
{{{$foo} {$foo}}}
|
|
62
|
+
")
|
|
63
|
+
msg.format(formatters={'side_effect': formatter_with_side_effect})
|
|
64
|
+
print(counter) # -> 2
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Function composition
|
|
68
|
+
|
|
69
|
+
Composition of functions is only allowed for the same functions i.e.
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
.local $x {42 :integer}
|
|
73
|
+
{{x = {$x :integer}}}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
.local $x {42 :integer}
|
|
78
|
+
{{x = {$x :string}}}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Options are merged together with the later options overwritting any earlier options
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
.local $date {|2024-05-10| :date style=short}
|
|
85
|
+
{{date = {$date :date style=full}}}
|
|
86
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
## Installation
|
|
2
|
+
|
|
3
|
+
This package is available via `pip` for Python `>= 3.12`:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pip install messageformat2
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Simple messages
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from messageformat2 import format_message
|
|
13
|
+
|
|
14
|
+
format_message("Hello, {$name}!", {"name": "Alice"}) # -> "Hello, Alice!"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Messages can be reused with different inputs.
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from messageformat2 import Message
|
|
21
|
+
|
|
22
|
+
message = Message("Hello, {$name}!")
|
|
23
|
+
message.format({"name": "Alice"}) # -> "Hello, Alice!"
|
|
24
|
+
message.format({"name": "Bob"}) # -> "Hello, Bob!"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Built-in formatters
|
|
28
|
+
|
|
29
|
+
There are several builtin locale-aware [formatters](/builtins) available.
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from datetime import datetime
|
|
33
|
+
from messageformat2 import Message
|
|
34
|
+
|
|
35
|
+
message = Message("Today's date is {$now :date}")
|
|
36
|
+
now = datetime.now()
|
|
37
|
+
message.format({"now": now}, locale='en_US')
|
|
38
|
+
# -> Today's date is 5/10/2024
|
|
39
|
+
message.format({"now": now}, locale='en_GB')
|
|
40
|
+
# -> Today's date is 10/05/2024
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Plural support
|
|
44
|
+
|
|
45
|
+
MF2 supports pluralization using built-in or custom selectors.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from messageformat2 import Message
|
|
49
|
+
|
|
50
|
+
msg = Message ("""\
|
|
51
|
+
.match {$count :number}
|
|
52
|
+
one {{You have {$count} notification.}}
|
|
53
|
+
* {{You have {$count} notifications.}}
|
|
54
|
+
""")
|
|
55
|
+
|
|
56
|
+
msg.format({"count": 42}, locale='en_US') # -> "You have 42 notifications."
|
|
57
|
+
msg.format({"count": 1}, locale='en_US') # -> "You have 1 notification."
|
|
58
|
+
```
|