|
| 1 | +# Design Proposal: Namespaces for Extending Functions, Options, etc. |
| 2 | + |
| 3 | +Status: **Accepted** |
| 4 | + |
| 5 | +<details> |
| 6 | + <summary>Metadata</summary> |
| 7 | + <dl> |
| 8 | + <dt>Contributors</dt> |
| 9 | + <dd>@aphillips</dd> |
| 10 | + <dt>First proposed</dt> |
| 11 | + <dd>2023-09-13</dd> |
| 12 | + <dt>Pull Request</dt> |
| 13 | + <dd>#475</dd> |
| 14 | + </dl> |
| 15 | +</details> |
| 16 | + |
| 17 | +## Objective |
| 18 | + |
| 19 | +_What is this proposal trying to achieve?_ |
| 20 | + |
| 21 | +This design defines how externally-authored functions can appear in a _message_; |
| 22 | +how externally-authored function options (and their values) can be supported; |
| 23 | +and what, if any, effects this has on the namespace of functions and options. |
| 24 | + |
| 25 | +Implementations will provide the functionality for selection and formatting, |
| 26 | +including options and option values. |
| 27 | +Much of this functionality will be mandated by the default registry. |
| 28 | +We expect that default registry entries will serve the core needs for MF2 users. |
| 29 | +However, there are many capabilities available in platform, library, |
| 30 | +or operating environment APIs that could be useful to developers and translators |
| 31 | +or which might be expected on a specific platform or in a specific programming language. |
| 32 | +In addition, we expect to provide support for markup and templating regimes. |
| 33 | +These need to be implemented using values not found in the default registry. |
| 34 | + |
| 35 | +An additional hope is that a robust ecosystem of function libraries will be created. |
| 36 | +A successful ecosystem will allow users to pick-and-choose or cherry-pick functions or |
| 37 | +options to use in a given development environment. |
| 38 | +Each function, option, or option value extension needs to work as seamlessly as possible |
| 39 | +with other add-ons and with the built-in functionality. |
| 40 | + |
| 41 | +## Background |
| 42 | + |
| 43 | +_What context is helpful to understand this proposal?_ |
| 44 | + |
| 45 | +One example of potential add-on functionality that can help readers understand this proposal |
| 46 | +is the use of _skeletons_ for date and number formatting. |
| 47 | + |
| 48 | +The JavaScript `Intl.DateTimeFormat` API provides for the customization of date and time values |
| 49 | +through the use of "option bags". For example: |
| 50 | + |
| 51 | +```js |
| 52 | +new Intl.DateTimeFormat("en-GB", { |
| 53 | + weekday: "long", |
| 54 | + year: "numeric", |
| 55 | + month: "short", |
| 56 | + day: "numeric", |
| 57 | + hour: "numeric", |
| 58 | + minute: "numeric", |
| 59 | +}); |
| 60 | +``` |
| 61 | + |
| 62 | +This might be expressed in MessageFormat as: |
| 63 | + |
| 64 | +``` |
| 65 | +Today is {$today :datetime weekday=long year=numeric month=short day=numeric hour=numeric minute=numeric} |
| 66 | +``` |
| 67 | + |
| 68 | +The ICU family of libraries provide a shorthand mechanism called _skeletons_ for accessing |
| 69 | +date and time format options without needing a verbose list of options. |
| 70 | +The same message might look like this with a skeleton: |
| 71 | + |
| 72 | +``` |
| 73 | +Today is {$today :datetime skeleton=EEEEyMdjm} |
| 74 | +``` |
| 75 | + |
| 76 | +Skeletons are not proposed for inclusion in the default registry |
| 77 | +because they are not universally available in all datetime formatting |
| 78 | +libraries. |
| 79 | + |
| 80 | +## Use-Cases |
| 81 | + |
| 82 | +_What use-cases do we see? Ideally, quote concrete examples._ |
| 83 | + |
| 84 | +- Developers need to add options to the base functions to suit local needs. |
| 85 | + For example, ICU's skeletons as part of the `:datetime` function |
| 86 | + Support for this option needs to be specified for local implemented versions. |
| 87 | + |
| 88 | +- Developers want to write a function and access it from messages. |
| 89 | + |
| 90 | +- Developers want to import 3rd party formatting packages and use the package's |
| 91 | + features from within messages. |
| 92 | + |
| 93 | +- Users want to import two or more formatting packages |
| 94 | + and these might have the same-named functions. |
| 95 | + For example, there might be both an HTML `p` and TTS `p` |
| 96 | + function. |
| 97 | + |
| 98 | +- Users want to control how extensions are referenced in their messages. |
| 99 | + For example, they might wish to make a long namespace name shorter. |
| 100 | + |
| 101 | +- Translators and tools would like a machine-readable way to find out the names |
| 102 | + and option values for add-on packages. |
| 103 | + |
| 104 | +## Requirements |
| 105 | + |
| 106 | +_What properties does the solution have to manifest to enable the use-cases above?_ |
| 107 | + |
| 108 | +- Developers must be able to write functions that do not later collide with items in the default registry. |
| 109 | +- Developers must be able to write function add-ons that do not later collide with items in the default registry. |
| 110 | +- Users should be able to tell visually when an add-on feature has been used vs. a built-in feature |
| 111 | +- Users should be able to resolve conflicts between add-on packages that use the same |
| 112 | + function names without altering add-on packages |
| 113 | + |
| 114 | +## Constraints |
| 115 | + |
| 116 | +_What prior decisions and existing conditions limit the possible design?_ |
| 117 | + |
| 118 | +- A syntactical prefix or its separator(s) must not collide with characters valid in either |
| 119 | + the prefix or in any of the name productions. |
| 120 | + |
| 121 | +- A prefix must not collide with unquoted literal values. |
| 122 | + |
| 123 | +## Proposed Design |
| 124 | + |
| 125 | +_Describe the proposed solution. Consider syntax, formatting, errors, registry, tooling, interchange._ |
| 126 | + |
| 127 | +The actual addition and provisioning of features to an implementation is implementation specific. |
| 128 | +Implementations are not required to read the registry format defined by MFv2 |
| 129 | +or use it for any particular purpose. |
| 130 | + |
| 131 | +> For example, a Java implementation might use the `ServiceProvider` interface to load |
| 132 | +> functionality, while a Node application might use `import`. |
| 133 | +
|
| 134 | +If an implementation supports user-installed formatters, selectors, function options, |
| 135 | +or expression annotations, it must also support providing "namespace" prefixes for |
| 136 | +each installed set of functionality. |
| 137 | + |
| 138 | +In this design, each namespace prefix is a short string and is associated with a URL. |
| 139 | +The URL is intended to point to some publically-available copy of the add-on library's |
| 140 | +registry description, for use by tooling and as a reference to users such as translators. |
| 141 | + |
| 142 | +There is no requirement that an implementation read the document at the end of the URL, |
| 143 | +nor validate the contents in general or against the actual functionality installed. |
| 144 | + |
| 145 | +> [!NOTE] |
| 146 | +> It is a good idea to use ASCII strings for namespace identifiers. |
| 147 | +> Remember that translators (and others) in many different languages and |
| 148 | +> with many different keyboards need to be able to enter the prefix. |
| 149 | +
|
| 150 | +The namespace prefix is part of the `name` production. |
| 151 | +The prefix must be at least one character in length. |
| 152 | +It may be as long as desired, although users are cautioned that brevity |
| 153 | +is desirable. |
| 154 | +The prefix is separated from the name by a colon (U+003A COLON). |
| 155 | + |
| 156 | +The default namespace is called the _anonymous namespace_. |
| 157 | +Names without a namespace prefix are in the anonymous namespace. |
| 158 | +Resolution of which function (or option, annotation, or spannable) is used |
| 159 | +for a name in the anonymous namesapce is implementation defined. |
| 160 | +Generally it will consist of the value in the default registry |
| 161 | +or the implementation's default. |
| 162 | + |
| 163 | +The choice of a `:` is intentional, as it already used for function identification |
| 164 | +and might be familiar from similar usage in XML namespaces |
| 165 | +as well as slightly similar to C++, e.g. `ns::function`. |
| 166 | +This design leverages these sorts of "application familiarity" |
| 167 | +as well as the current syntax's use of colon as the function sigil. |
| 168 | + |
| 169 | +```abnf |
| 170 | +name = [namespace] name-body |
| 171 | +namespace = name-start *name-char namespace-sep |
| 172 | +namespace-sep = ":" |
| 173 | +name-body = name-start *name-char |
| 174 | +``` |
| 175 | + |
| 176 | +> [!NOTE] |
| 177 | +> The `name-start` and `name-char` productions will have to be altered to |
| 178 | +> **_not_** permit U+003A COLON in a name and to otherwise address |
| 179 | +> naming concerns. |
| 180 | +> This design document does not show the naming changes because there are |
| 181 | +> other issues in play for these names. |
| 182 | +> For now, just consider that `name-char` will have no colon. |
| 183 | +
|
| 184 | +The `name` production as defined here applies to: |
| 185 | + |
| 186 | +- function (selector/formatting) names |
| 187 | +- option names |
| 188 | +- spannable names |
| 189 | +- expression annotation names (if approved) |
| 190 | + |
| 191 | +Examples: |
| 192 | + |
| 193 | +> Add-on function: |
| 194 | +> |
| 195 | +> ``` |
| 196 | +> Today is {$today :icu:datetime dateStyle=long} |
| 197 | +> ``` |
| 198 | +> |
| 199 | +> Add-on option: |
| 200 | +> |
| 201 | +> ``` |
| 202 | +> Today is {$today :datetime icu:skeleton=EEEEyMdjm} |
| 203 | +> ``` |
| 204 | +> |
| 205 | +> Add-on spannables (such as markup): |
| 206 | +> |
| 207 | +> ``` |
| 208 | +> Today is {+html:a}{$today}{-html:a} |
| 209 | +> ``` |
| 210 | +> |
| 211 | +> Add-on expression annotation: |
| 212 | +> |
| 213 | +> ``` |
| 214 | +> Today is {$today :datetime @my:annotation} |
| 215 | +> ``` |
| 216 | +> |
| 217 | +> Everything altogether all at once. This probably does not work |
| 218 | +> correctly, since `:foo:datetime` may not understand `icu:skeleton`: |
| 219 | +> |
| 220 | +> ``` |
| 221 | +> Today is {+html:a}{$today :foo:datetime icu:skeleton=EEEEyMdjm @my:annotation}{-html:a} |
| 222 | +> ``` |
| 223 | +
|
| 224 | +Users, such as developers writing messages or translators creating translations, |
| 225 | +are not required to type the namespace prefix in message patterns unless there |
| 226 | +is ambiguity in the given formatting content or in the runtime. |
| 227 | +However, tooling might reject or have difficulty processing values without |
| 228 | +the prefix being present. |
| 229 | +
|
| 230 | +> For example, if an implementation is using the ICU4J library, any of the |
| 231 | +> following messages might be acceptable alternatives: |
| 232 | +> |
| 233 | +> ``` |
| 234 | +> Today is {$today :datetime skeleton=EEEEMd} |
| 235 | +> Today is {$today :datetime icu:skeleton=EEEEMd} |
| 236 | +> Today is {$today :icu:datetime skeleton=EEEEMd} |
| 237 | +> Today is {$today :icu:datetime icu:skeleton=EEEEMd} |
| 238 | +> ``` |
| 239 | +
|
| 240 | +### Changes Required by This Design |
| 241 | +
|
| 242 | +Implementation of this design will require the following changes: |
| 243 | +
|
| 244 | +- Update the ABNF syntax and corresponding text in the syntax.md spec |
| 245 | +- Changes to the `name`/`name-char` productions and related naming productions |
| 246 | +- Additions to the formatting.md spec regarding namespace resolution |
| 247 | + to ensure that the correct function is called |
| 248 | +- Additional error type for namespace resolution failure; alternatively |
| 249 | + this might take the form of the existing resolution error |
| 250 | +- Addition of namespace to the data model for all relative items |
| 251 | +
|
| 252 | +It is possible that the registry description will need to include slots for URL |
| 253 | +and default namespace name. |
| 254 | +
|
| 255 | +### Potential Negatives |
| 256 | +
|
| 257 | +This design is based on the assertion that implementors will provide an |
| 258 | +extension mechanism and that users will want to use that mechanism to install formatting |
| 259 | +or selection functionality. |
| 260 | +Any non-standard functions, options, option values, or expressions have the potential |
| 261 | +to be disruptive or fragmenting to the overall tooling or localization space. |
| 262 | +Any extension that is widely adopted would thus be better off in the default registry |
| 263 | +if at all possible. |
| 264 | +On the other hand, language- or platform-specific extensions can make MFv2 feel |
| 265 | +more "fluent" or consistent for users in a given environment. |
| 266 | +
|
| 267 | +## Alternatives Considered |
| 268 | +
|
| 269 | +_What other solutions are available?_ |
| 270 | +_How do they compare against the requirements?_ |
| 271 | +_What other properties they have?_ |
| 272 | +
|
| 273 | +### Choice of namespace separator |
| 274 | +
|
| 275 | +The choice of the `:` character as a separator is, as noted above, a considered part |
| 276 | +of the design, but it is not required. |
| 277 | +Other characters are possible for the same purpose. |
| 278 | +
|
| 279 | +A concern is that `name-char` reserves `:`, `-`, and `.`, which are probably the |
| 280 | +most likely candidates. |
| 281 | +Choosing one of these characters would require altering the `name-char` (and |
| 282 | +thus `name`) production. |
| 283 | +Moving to use `NCName` instead of `Nmtoken` as a basis for `name` would address |
| 284 | +the use of `:`. |
| 285 | +Moving to `-` or `.` would not require alterations of the syntax if we decided |
| 286 | +that namespacing is not formally a part of the specification but, rather, is |
| 287 | +just a convention. |
| 288 | +
|
| 289 | +Of the remaining characters not currently in use, the most obvious candidates would |
| 290 | +be `/` and `~`. |
| 291 | +
|
| 292 | +> Here are examples of the options: |
| 293 | +> |
| 294 | +> ``` |
| 295 | +> {$today :ns:function ns:option=foo}{+ns:a}{-ns:a} |
| 296 | +> {$today :ns-function ns-option=foo}{+ns-a}{-ns-a} |
| 297 | +> {$today :ns.function ns.option=foo}{+ns.a}{-ns.a} |
| 298 | +> {$today :ns/function ns/option=foo}{+ns/a}{-ns/a} |
| 299 | +> {$today :ns~function ns~option=foo}{+ns~a}{-ns~a} |
| 300 | +> ``` |
| 301 | +
|
| 302 | +### No namespacing |
| 303 | +
|
| 304 | +Each implementation can install whatever additional functionality. |
| 305 | +It is up to the implementation to describe what is permitted and to check messages. |
| 306 | +Users will have to RTFM. |
| 307 | +
|
| 308 | +- **+** Flexible |
| 309 | +- **-** Does not promote a healthy ecosystem of add-on packages |
| 310 | +- **-** Does not supply a mechanism for tooling to leverage |
| 311 | +
|
| 312 | +### Reverse-domain-name namespacing |
| 313 | +
|
| 314 | +Use `com.foo.bar.baz.Function` type naming for functions, options, or expressions. |
| 315 | +
|
| 316 | +> ``` |
| 317 | +> Today is {$today :com.example.foo.datetime dateStyle=short} |
| 318 | +> Today is {$today :datetime com.example.foo.skeleton=EEEEMd} |
| 319 | +> ``` |
| 320 | +
|
| 321 | +- **+** Familiarity. This is a familiar structure for developers. |
| 322 | +- **-** Verbose. The resulting names are long and difficult to parse visually |
| 323 | +
|
| 324 | +### Namespacing sigils are customary not normative |
| 325 | +
|
| 326 | +Imported namespace names are agglutinated to the function/option/spannable/attribute |
| 327 | +name with no separator. |
| 328 | +The use of a separator, such as `:` or `.` is a convention used by developers |
| 329 | +or users when importing the namespace. |
| 330 | +Thus, a user could import a library as `foo` or as `foo:`, resulting in examples like: |
| 331 | +
|
| 332 | +> ``` |
| 333 | +> {$today :foofunction foooption=foo}{+fooa}{-fooa} with just "foo" |
| 334 | +> {$today :foo:function foo:option=foo}{+foo:a}{-foo:a} with prefix "foo:" |
| 335 | +> {$today :foo:-:function foo:-:option=foo}{+foo:-:a}{-foo:-:a} users can use any legal characters |
| 336 | +> ``` |
0 commit comments