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.
Files changed (72) hide show
  1. messageformat2-0.1.0/.github/workflows/ci.yml +34 -0
  2. messageformat2-0.1.0/.gitignore +10 -0
  3. messageformat2-0.1.0/.readthedocs.yaml +19 -0
  4. messageformat2-0.1.0/CONTRIBUTING.md +29 -0
  5. messageformat2-0.1.0/LICENSE +21 -0
  6. messageformat2-0.1.0/PKG-INFO +148 -0
  7. messageformat2-0.1.0/README.md +89 -0
  8. messageformat2-0.1.0/docs/builtins.md +172 -0
  9. messageformat2-0.1.0/docs/datamodel.md +10 -0
  10. messageformat2-0.1.0/docs/errors.md +4 -0
  11. messageformat2-0.1.0/docs/explanation.md +86 -0
  12. messageformat2-0.1.0/docs/getting_started.md +58 -0
  13. messageformat2-0.1.0/docs/howto/datamodel.md +60 -0
  14. messageformat2-0.1.0/docs/howto/errors.md +48 -0
  15. messageformat2-0.1.0/docs/howto/formatter.md +51 -0
  16. messageformat2-0.1.0/docs/howto/selector.md +66 -0
  17. messageformat2-0.1.0/docs/index.md +37 -0
  18. messageformat2-0.1.0/docs/message.md +9 -0
  19. messageformat2-0.1.0/docs/mf2.md +13 -0
  20. messageformat2-0.1.0/docs/reference.md +12 -0
  21. messageformat2-0.1.0/messageformat2/__init__.py +5 -0
  22. messageformat2-0.1.0/messageformat2/builtins.py +323 -0
  23. messageformat2-0.1.0/messageformat2/cli.py +51 -0
  24. messageformat2-0.1.0/messageformat2/datamodel.py +507 -0
  25. messageformat2-0.1.0/messageformat2/errors.py +151 -0
  26. messageformat2-0.1.0/messageformat2/message.py +127 -0
  27. messageformat2-0.1.0/messageformat2/parser.py +557 -0
  28. messageformat2-0.1.0/messageformat2/runtime.py +301 -0
  29. messageformat2-0.1.0/messageformat2.egg-info/PKG-INFO +148 -0
  30. messageformat2-0.1.0/messageformat2.egg-info/SOURCES.txt +70 -0
  31. messageformat2-0.1.0/messageformat2.egg-info/dependency_links.txt +1 -0
  32. messageformat2-0.1.0/messageformat2.egg-info/entry_points.txt +2 -0
  33. messageformat2-0.1.0/messageformat2.egg-info/requires.txt +20 -0
  34. messageformat2-0.1.0/messageformat2.egg-info/top_level.txt +1 -0
  35. messageformat2-0.1.0/mkdocs.yml +50 -0
  36. messageformat2-0.1.0/pyproject.toml +92 -0
  37. messageformat2-0.1.0/requirements.dev.txt +176 -0
  38. messageformat2-0.1.0/requirements.docs.txt +96 -0
  39. messageformat2-0.1.0/requirements.txt +4 -0
  40. messageformat2-0.1.0/setup.cfg +4 -0
  41. messageformat2-0.1.0/tests/__init__.py +0 -0
  42. messageformat2-0.1.0/tests/data/complex_message_1.txt +31 -0
  43. messageformat2-0.1.0/tests/data/complex_message_2.txt +27 -0
  44. messageformat2-0.1.0/tests/data/complex_message_3.txt +22 -0
  45. messageformat2-0.1.0/tests/data/simple_message_1.txt +1 -0
  46. messageformat2-0.1.0/tests/data/simple_message_10.txt +10 -0
  47. messageformat2-0.1.0/tests/data/simple_message_11.txt +12 -0
  48. messageformat2-0.1.0/tests/data/simple_message_12.txt +10 -0
  49. messageformat2-0.1.0/tests/data/simple_message_13.txt +8 -0
  50. messageformat2-0.1.0/tests/data/simple_message_14.txt +10 -0
  51. messageformat2-0.1.0/tests/data/simple_message_15.txt +15 -0
  52. messageformat2-0.1.0/tests/data/simple_message_16.txt +10 -0
  53. messageformat2-0.1.0/tests/data/simple_message_2.txt +1 -0
  54. messageformat2-0.1.0/tests/data/simple_message_3.txt +3 -0
  55. messageformat2-0.1.0/tests/data/simple_message_4.txt +4 -0
  56. messageformat2-0.1.0/tests/data/simple_message_5.txt +7 -0
  57. messageformat2-0.1.0/tests/data/simple_message_6.txt +11 -0
  58. messageformat2-0.1.0/tests/data/simple_message_7.txt +18 -0
  59. messageformat2-0.1.0/tests/data/simple_message_8.txt +12 -0
  60. messageformat2-0.1.0/tests/data/simple_message_9.txt +15 -0
  61. messageformat2-0.1.0/tests/data/simple_message_escapes_1.txt +1 -0
  62. messageformat2-0.1.0/tests/data/simple_message_escapes_2.txt +1 -0
  63. messageformat2-0.1.0/tests/data/unsupported_annotations_1.txt +4 -0
  64. messageformat2-0.1.0/tests/data/unsupported_annotations_2.txt +4 -0
  65. messageformat2-0.1.0/tests/data/unsupported_annotations_3.txt +3 -0
  66. messageformat2-0.1.0/tests/data/unsupported_annotations_4.txt +6 -0
  67. messageformat2-0.1.0/tests/data/unsupported_annotations_5.txt +8 -0
  68. messageformat2-0.1.0/tests/data/unsupported_statement_1.txt +15 -0
  69. messageformat2-0.1.0/tests/data/unsupported_statement_2.txt +21 -0
  70. messageformat2-0.1.0/tests/test_parser.py +235 -0
  71. messageformat2-0.1.0/tests/test_regex.py +210 -0
  72. 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,10 @@
1
+ venv/
2
+ .python-version
3
+ __pycache__/
4
+ .coverage
5
+ htmlcov/
6
+ .ruff_cache/
7
+ .pytest_cache/
8
+ *.egg-info/
9
+
10
+ node_modules/
@@ -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 [![Documentation Status](https://readthedocs.org/projects/messageformat2/badge/?version=latest)](https://messageformat2.readthedocs.io/en/latest/?badge=latest) [![CI](https://github.com/tomasr8/messageformat2/workflows/CI/badge.svg)](https://github.com/tomasr8/messageformat2/workflows/CI/badge.svg) [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_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 [![Documentation Status](https://readthedocs.org/projects/messageformat2/badge/?version=latest)](https://messageformat2.readthedocs.io/en/latest/?badge=latest) [![CI](https://github.com/tomasr8/messageformat2/workflows/CI/badge.svg)](https://github.com/tomasr8/messageformat2/workflows/CI/badge.svg) [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_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,10 @@
1
+ ::: messageformat2.datamodel
2
+ options:
3
+ show_root_members_full_path: true
4
+ show_if_no_docstring: True
5
+ members_order: source
6
+ filters:
7
+ - "!^__"
8
+ - "!^_Matcher"
9
+ - "!^_generic_visit"
10
+ - "!^DataModelValidator"
@@ -0,0 +1,4 @@
1
+ ::: messageformat2.errors
2
+ options:
3
+ members_order: source
4
+
@@ -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
+ ```