python/moz/l10n/formats/mf2/to_json.py (99 lines of code) (raw):

# Copyright Mozilla Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations from typing import Any, Literal from ...model import ( CatchallKey, Expression, Markup, Message, Pattern, PatternMessage, SelectMessage, VariableRef, ) def mf2_to_json(message: Message) -> dict[str, Any]: """ Represent a message using the MessageFormat 2 data model [JSON Schema](https://github.com/unicode-org/message-format-wg/blob/main/spec/data-model/message.json). Does not validate the message; for that, use `mf2_validate_message()`. """ json_declarations = [ { "type": ( "input" if isinstance(expr.arg, VariableRef) and expr.arg.name == name else "local" ), "name": name, "value": _expression(expr), } for name, expr in message.declarations.items() ] if isinstance(message, PatternMessage): return { "type": "message", "declarations": json_declarations, "pattern": _pattern(message.pattern), } else: assert isinstance(message, SelectMessage) return { "type": "select", "declarations": json_declarations, "selectors": [_variable(sel) for sel in message.selectors], "variants": [ {"keys": [_key(key) for key in keys], "value": _pattern(pattern)} for keys, pattern in message.variants.items() ], } def _pattern(pattern: Pattern) -> list[Any]: return [ part if isinstance(part, str) else _markup(part) if isinstance(part, Markup) else _expression(part) for part in pattern ] def _expression(expr: Expression) -> dict[str, str | dict[str, Any]]: json: dict[str, Any] = {"type": "expression"} if expr.arg is not None: json["arg"] = _value(expr.arg) if expr.function is not None: json_func: dict[str, Any] = {"type": "function", "name": expr.function} if expr.options: json_func["options"] = _options(expr.options) json["function"] = json_func if expr.attributes: json["attributes"] = _attributes(expr.attributes) return json def _markup(markup: Markup) -> dict[str, str | dict[str, Any]]: json: dict[str, Any] = { "type": "markup", "kind": markup.kind, "name": markup.name, } if markup.options: json["options"] = _options(markup.options) if markup.attributes: json["attributes"] = _attributes(markup.attributes) return json def _options( options: dict[str, str | VariableRef], ) -> dict[str, dict[str, str]]: return {name: _value(value) for name, value in options.items()} def _attributes( attributes: dict[str, str | Literal[True]], ) -> dict[str, dict[str, str] | Literal[True]]: return { name: True if value is True else _literal(value) for name, value in attributes.items() } def _key(key: str | CatchallKey) -> str | dict[str, str]: if isinstance(key, str): return _literal(key) else: json = {"type": "*"} if key.value is not None: json["value"] = key.value return json def _value(value: str | VariableRef) -> dict[str, str]: return _literal(value) if isinstance(value, str) else _variable(value) def _literal(value: str) -> dict[str, str]: return {"type": "literal", "value": value} def _variable(var: VariableRef) -> dict[str, str]: return {"type": "variable", "name": var.name}