Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

## Table of Contents

- [What's new (2026-06-22) — ICU-lite MessageFormat (Plural / Select)](#whats-new-2026-06-22--icu-lite-messageformat-plural--select)
- [What's new (2026-06-22) — Locale-Aware List Formatting](#whats-new-2026-06-22--locale-aware-list-formatting)
- [What's new (2026-06-22) — Bidirectional-Text QA (Trojan-Source Scan)](#whats-new-2026-06-22--bidirectional-text-qa-trojan-source-scan)
- [What's new (2026-06-22) — Readability Scoring](#whats-new-2026-06-22--readability-scoring)
Expand Down Expand Up @@ -165,6 +166,12 @@

---

## What's new (2026-06-22) — ICU-lite MessageFormat (Plural / Select)

Render count-aware localised messages. Full reference: [`docs/source/Eng/doc/new_features/v113_features_doc.rst`](docs/source/Eng/doc/new_features/v113_features_doc.rst).

- **`format_message` / `plural_category` / `ordinal_category`** (`AC_format_message`): `i18n_test.check_catalog` only compares placeholder sets and `interpolate` is flat `${var}` — neither renders `"{count, plural, one {# item} other {# items}}"`. This implements the ICU MessageFormat subset most apps use: `select`, `plural`, `selectordinal` with CLDR categories, exact `=N` selectors, the `#` count, `offset:`, nesting and apostrophe quoting. Injectable plural rules. Pure-stdlib, deterministic.

## What's new (2026-06-22) — Locale-Aware List Formatting

Join items the way a language expects ("A, B, and C"). Full reference: [`docs/source/Eng/doc/new_features/v112_features_doc.rst`](docs/source/Eng/doc/new_features/v112_features_doc.rst).
Expand Down
7 changes: 7 additions & 0 deletions README/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目录

- [本次更新 (2026-06-22) — ICU-lite MessageFormat(复数 / 选择)](#本次更新-2026-06-22--icu-lite-messageformat复数--选择)
- [本次更新 (2026-06-22) — 区域感知列表格式化](#本次更新-2026-06-22--区域感知列表格式化)
- [本次更新 (2026-06-22) — 双向文字 QA(Trojan-Source 扫描)](#本次更新-2026-06-22--双向文字-qatrojan-source-扫描)
- [本次更新 (2026-06-22) — 可读性评分](#本次更新-2026-06-22--可读性评分)
Expand Down Expand Up @@ -168,6 +169,12 @@

平滑噪声值序列。完整参考:[`docs/source/Zh/doc/new_features/v102_features_doc.rst`](../docs/source/Zh/doc/new_features/v102_features_doc.rst)。

## 本次更新 (2026-06-22) — ICU-lite MessageFormat(复数 / 选择)

渲染依数量变化的在地化消息。完整参考:[`docs/source/Zh/doc/new_features/v113_features_doc.rst`](../docs/source/Zh/doc/new_features/v113_features_doc.rst)。

- **`format_message` / `plural_category` / `ordinal_category`**(`AC_format_message`):`i18n_test.check_catalog` 只比较占位符集合、`interpolate` 只做扁平 `${var}`——两者都无法渲染 `"{count, plural, one {# item} other {# items}}"`。本功能实作多数应用会用到的 ICU MessageFormat 子集:`select`、`plural`、`selectordinal` 搭配 CLDR 类别、优先于类别的精确 `=N` 选择器、`#` 数量、`offset:`、嵌套与单引号转义。复数规则可注入。纯标准库、确定。

## 本次更新 (2026-06-22) — 区域感知列表格式化

依某语言的期望串接项目(「A、B and C」)。完整参考:[`docs/source/Zh/doc/new_features/v112_features_doc.rst`](../docs/source/Zh/doc/new_features/v112_features_doc.rst)。
Expand Down
7 changes: 7 additions & 0 deletions README/README_zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目錄

- [本次更新 (2026-06-22) — ICU-lite MessageFormat(複數 / 選擇)](#本次更新-2026-06-22--icu-lite-messageformat複數--選擇)
- [本次更新 (2026-06-22) — 地區感知清單格式化](#本次更新-2026-06-22--地區感知清單格式化)
- [本次更新 (2026-06-22) — 雙向文字 QA(Trojan-Source 掃描)](#本次更新-2026-06-22--雙向文字-qatrojan-source-掃描)
- [本次更新 (2026-06-22) — 可讀性評分](#本次更新-2026-06-22--可讀性評分)
Expand Down Expand Up @@ -168,6 +169,12 @@

平滑雜訊值序列。完整參考:[`docs/source/Zh/doc/new_features/v102_features_doc.rst`](../docs/source/Zh/doc/new_features/v102_features_doc.rst)。

## 本次更新 (2026-06-22) — ICU-lite MessageFormat(複數 / 選擇)

渲染依數量變化的在地化訊息。完整參考:[`docs/source/Zh/doc/new_features/v113_features_doc.rst`](../docs/source/Zh/doc/new_features/v113_features_doc.rst)。

- **`format_message` / `plural_category` / `ordinal_category`**(`AC_format_message`):`i18n_test.check_catalog` 只比較佔位符集合、`interpolate` 只做扁平 `${var}`——兩者都無法渲染 `"{count, plural, one {# item} other {# items}}"`。本功能實作多數應用會用到的 ICU MessageFormat 子集:`select`、`plural`、`selectordinal` 搭配 CLDR 類別、優先於類別的精確 `=N` 選擇器、`#` 數量、`offset:`、巢狀與單引號跳脫。複數規則可注入。純標準函式庫、具決定性。

## 本次更新 (2026-06-22) — 地區感知清單格式化

依某語言的期望串接項目(「A、B and C」)。完整參考:[`docs/source/Zh/doc/new_features/v112_features_doc.rst`](../docs/source/Zh/doc/new_features/v112_features_doc.rst)。
Expand Down
46 changes: 46 additions & 0 deletions docs/source/Eng/doc/new_features/v113_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
ICU-lite MessageFormat (Plural / Select)
========================================

``i18n_test.check_catalog`` only compares placeholder *sets* and ``interpolate``
does flat ``${var}`` substitution — neither can render the count-aware messages
real localisation needs, e.g. ``"{count, plural, one {# item} other {# items}}"``.
This implements the ICU MessageFormat subset most apps use.

Pure standard library; imports no ``PySide6``. The plural/ordinal category
functions are pure and the rule callables are injectable, so rendering is fully
deterministic in CI.

Headless API
------------

.. code-block:: python

from je_auto_control import format_message, plural_category, ordinal_category

plural = "{count, plural, one {# item} other {# items}}"
format_message(plural, {"count": 1}) # '1 item'
format_message(plural, {"count": 5}) # '5 items'

select = "{g, select, male {He} female {She} other {They}} won"
format_message(select, {"g": "female"}) # 'She won'

ordinal = "{place, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}"
format_message(ordinal, {"place": 3}) # '3rd'

plural_category(2) # 'other'
ordinal_category(3) # 'few'

Supported: simple ``{name}`` arguments, ``select`` (e.g. gender), ``plural`` and
``selectordinal`` with the CLDR categories (``zero``/``one``/``two``/``few``/
``many``/``other``), exact ``=N`` selectors that win over a category, the ``#``
count placeholder, a plural ``offset:`` (``#`` becomes count − offset), nested
arguments, and ICU apostrophe quoting (``''`` → ``'``; ``'{'`` → literal brace).
``plural_rules`` / ``ordinal_rules`` let you inject custom category functions;
``locale`` selects the built-ins (``en``, ``fr``).

Executor commands
-----------------

``AC_format_message`` takes a ``pattern`` plus a JSON ``args`` object and returns
``{text}``, accepting ``locale``. It is exposed as the MCP tool
``ac_format_message`` and as a Script Builder command under **Data**.
1 change: 1 addition & 0 deletions docs/source/Eng/eng_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ Comprehensive guides for all AutoControl features.
doc/new_features/v110_features_doc
doc/new_features/v111_features_doc
doc/new_features/v112_features_doc
doc/new_features/v113_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
40 changes: 40 additions & 0 deletions docs/source/Zh/doc/new_features/v113_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
ICU-lite MessageFormat(複數 / 選擇)
===================================

``i18n_test.check_catalog`` 只比較佔位符*集合*、``interpolate`` 只做扁平 ``${var}`` 取代——兩者都無法渲染
真正在地化所需的依數量變化訊息,例如 ``"{count, plural, one {# item} other {# items}}"``。本功能實作多數應用
會用到的 ICU MessageFormat 子集。

純標準函式庫;不匯入 ``PySide6``。複數/序數類別函式為純函式,且規則 callable 可注入,因此渲染在 CI 中
完全具決定性。

無頭 API
--------

.. code-block:: python

from je_auto_control import format_message, plural_category, ordinal_category

plural = "{count, plural, one {# item} other {# items}}"
format_message(plural, {"count": 1}) # '1 item'
format_message(plural, {"count": 5}) # '5 items'

select = "{g, select, male {He} female {She} other {They}} won"
format_message(select, {"g": "female"}) # 'She won'

ordinal = "{place, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}"
format_message(ordinal, {"place": 3}) # '3rd'

plural_category(2) # 'other'
ordinal_category(3) # 'few'

支援:簡單 ``{name}`` 參數、``select``(如性別)、``plural`` 與 ``selectordinal`` 搭配 CLDR 類別
(``zero``/``one``/``two``/``few``/``many``/``other``)、優先於類別的精確 ``=N`` 選擇器、``#`` 數量佔位符、
複數 ``offset:``(``#`` 變為 count − offset)、巢狀參數,以及 ICU 單引號跳脫(``''`` → ``'``;``'{'`` → 字面
大括號)。``plural_rules`` / ``ordinal_rules`` 可注入自訂類別函式;``locale`` 選擇內建規則(``en``、``fr``)。

執行器命令
----------

``AC_format_message`` 接受 ``pattern`` 與 JSON ``args`` 物件並回傳 ``{text}``,可帶 ``locale``。它以 MCP 工具
``ac_format_message`` 以及 Script Builder 中 **Data** 分類下的命令提供。
1 change: 1 addition & 0 deletions docs/source/Zh/zh_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ AutoControl 所有功能的完整使用指南。
doc/new_features/v110_features_doc
doc/new_features/v111_features_doc
doc/new_features/v112_features_doc
doc/new_features/v113_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
7 changes: 7 additions & 0 deletions je_auto_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@
from je_auto_control.utils.bidi_check import is_balanced as is_bidi_balanced
# Locale-aware list formatting ("A, B, and C") in the style of CLDR
from je_auto_control.utils.list_format import format_list
# ICU-lite MessageFormat (plural / select / selectordinal rendering)
from je_auto_control.utils.message_format import (
format_message, ordinal_category, plural_category,
)
# CI workflow annotations (GitHub Actions)
from je_auto_control.utils.ci_annotations import (
emit_annotations, format_annotation,
Expand Down Expand Up @@ -991,6 +995,9 @@ def start_autocontrol_gui(*args, **kwargs):
"is_trojan_source",
"strip_bidi_controls",
"format_list",
"format_message",
"ordinal_category",
"plural_category",
"emit_annotations", "format_annotation",
"ClipboardHistory", "default_clipboard_history",
"analyze_heal_log", "heal_stats", "scan_secrets",
Expand Down
11 changes: 11 additions & 0 deletions je_auto_control/gui/script_builder/command_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,17 @@ def _add_resilience_specs(specs: List[CommandSpec]) -> None:
),
description="Join items into a localised list ('A, B, and C').",
))
specs.append(CommandSpec(
"AC_format_message", "Data", "Text: Format Message (ICU)",
fields=(
FieldSpec("pattern", FieldType.STRING,
placeholder="{count, plural, one {# item} other {# items}}"),
FieldSpec("args", FieldType.STRING, placeholder='{"count": 3}'),
FieldSpec("locale", FieldType.STRING, optional=True,
placeholder="en | fr"),
),
description="Render ICU plural/select/selectordinal message.",
))
specs.append(CommandSpec(
"AC_diff_rows", "Data", "Dataset Diff: Rows by Key",
fields=(
Expand Down
11 changes: 11 additions & 0 deletions je_auto_control/utils/executor/action_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3021,6 +3021,16 @@ def _format_list(items: Any, style: str = "and",
return {"text": format_list(list(items), style=style, locale=locale)}


def _format_message(pattern: str, args: Any = None,
locale: str = "en") -> Dict[str, Any]:
"""Adapter: render an ICU-lite MessageFormat pattern."""
import json
from je_auto_control.utils.message_format import format_message
if isinstance(args, str):
args = json.loads(args)
return {"text": format_message(pattern, args or {}, locale=locale)}


def _cas_put(name: str, key: str, value: Any,
expected_version: Any = None) -> Dict[str, Any]:
"""Adapter: optimistic put into a named versioned store."""
Expand Down Expand Up @@ -4711,6 +4721,7 @@ def __init__(self):
"AC_bidi_check": _bidi_check,
"AC_bidi_strip": _bidi_strip,
"AC_format_list": _format_list,
"AC_format_message": _format_message,
"AC_detect_drift": _detect_drift,
"AC_categorical_drift": _categorical_drift,
"AC_diff_rows": _diff_rows,
Expand Down
19 changes: 18 additions & 1 deletion je_auto_control/utils/mcp_server/tools/_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -3682,6 +3682,23 @@ def list_format_tools() -> List[MCPTool]:
]


def message_format_tools() -> List[MCPTool]:
return [
MCPTool(
name="ac_format_message",
description=("Render an ICU-lite MessageFormat 'pattern' against "
"'args' (plural/select/selectordinal, =N, #). "
"'locale' picks plural rules. Returns {text}."),
input_schema=schema(
{"pattern": {"type": "string"}, "args": {"type": "object"},
"locale": {"type": "string"}},
["pattern"]),
handler=h.format_message,
annotations=READ_ONLY,
),
]


def bidi_check_tools() -> List[MCPTool]:
return [
MCPTool(
Expand Down Expand Up @@ -5744,7 +5761,7 @@ def media_assert_tools() -> List[MCPTool]:
timeseries_tools, anomaly_tools, smoothing_tools, idempotency_tools,
dedup_window_tools, sequence_gap_tools, optimistic_tools, outbox_tools,
locale_collation_tools, confusables_tools, readability_tools,
bidi_check_tools, list_format_tools,
bidi_check_tools, list_format_tools, message_format_tools,
dataset_diff_tools, referential_tools, link_header_tools, multipart_tools,
http_content_tools, cookie_jar_tools, http_conditional_tools,
saga_tools, decision_table_tools, locator_repair_tools,
Expand Down
5 changes: 5 additions & 0 deletions je_auto_control/utils/mcp_server/tools/_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2002,6 +2002,11 @@ def format_list(items, style="and", locale="en"):
return _format_list(items, style, locale)


def format_message(pattern, args=None, locale="en"):
from je_auto_control.utils.executor.action_executor import _format_message
return _format_message(pattern, args, locale)


def detect_drift(reference, current, threshold=0.25, bins=10):
from je_auto_control.utils.executor.action_executor import _detect_drift
return _detect_drift(reference, current, threshold, bins)
Expand Down
6 changes: 6 additions & 0 deletions je_auto_control/utils/message_format/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""ICU-lite MessageFormat (plural / select / selectordinal rendering)."""
from je_auto_control.utils.message_format.message_format import (
format_message, ordinal_category, plural_category,
)

__all__ = ["format_message", "ordinal_category", "plural_category"]
Loading
Loading