Tag a run's script slot (ASCII/Latin or East-Asian) with a language code so spell-check and hyphenation can pick the right dictionary.
Library verdicts: python-docx: — docxjs: —
| Feature id | docx/run-language--en-us--ea |
|---|---|
| Format | docx |
| Category | text-formatting |
| Family | docx/run-language |
| Axis values | lang=en-us, slot=ea |
| Spec | ecma-376-5-part-1 § 17.3.2.20 w:lang |
| ID / part | Predicate | XPath | python-docx | docxjs |
|---|---|---|---|---|
lang-element-present-en-us-eaword/document.xml | existThe run must carry a <w:lang> child inside <w:rPr>. | //w:r[w:t[normalize-space()='Lang tagged text']]/w:rPr/w:lang | — | — |
lang-ea-is-en-usword/document.xml | equal = en-USThe selected w:lang slot attribute must carry the expected BCP-47 language tag. | //w:r[w:t[normalize-space()='Lang tagged text']]/w:rPr/w:lang/@w:eastAsia | — | — |
| ID | Predicate | Selector | python-docx | docxjs |
|---|---|---|---|---|
lang-attribute-propagated-en-us-ea | css_selector/ existAccessibility: the w:lang tag on the run should surface somewhere in the rendered DOM — as a lang attribute on the root html, on the .docx-wrapper, on any descendant element (paragraph or run), or as an xml:lang attribute. Screen readers switch pronunciation on this hint; a renderer that drops it entirely fails this assertion. | html[lang], .docx-wrapper[lang], .docx-wrapper [lang], .docx-wrapper [xml\:lang] | — | — |
scripts/gen_run_language.py
#!/usr/bin/env python3
"""Generate a ``fixtures/docx/run-language--<lang>--<slot>.docx`` fixture.
Parameterised generator for the ``docx/run-language`` feature family.
One invocation writes a single fixture with a single run whose rPr
carries ``<w:lang/>`` with the target slot attribute set to the
requested BCP-47 language tag.
Usage::
python scripts/gen_run_language.py \\
--lang fr-FR \\
--slot val \\
--out fixtures/docx/run-language--fr-fr--ascii.docx
``--lang`` is a BCP-47 tag written into the selected slot attribute.
``--slot`` is the local name of the target ``w:lang`` attribute:
- ``val`` -> ``w:val`` (ASCII / Latin)
- ``eastAsia`` -> ``w:eastAsia`` (East-Asian)
- ``bidi`` -> ``w:bidi`` (complex-script / RTL)
python-docx has no high-level language API; the generator reaches
down to :class:`docx.oxml.OxmlElement` so each fixture only exercises
one slot.
"""
from __future__ import annotations
import argparse
from pathlib import Path
from docx import Document
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
_REPO_ROOT = Path(__file__).resolve().parent.parent
_VALID_SLOTS = {"val", "eastAsia", "bidi"}
def _write(lang: str, slot: str, out: Path) -> None:
if slot not in _VALID_SLOTS:
raise ValueError(
f"Unknown slot {slot!r}; expected one of {sorted(_VALID_SLOTS)}"
)
doc = Document()
paragraph = doc.add_paragraph()
run = paragraph.add_run("Lang tagged text")
rPr = run._r.get_or_add_rPr()
# Keep the element a singleton — if a previous setter added w:lang,
# remove it before appending ours.
for existing in rPr.findall(qn("w:lang")):
rPr.remove(existing)
lang_el = OxmlElement("w:lang")
lang_el.set(qn(f"w:{slot}"), lang)
rPr.append(lang_el)
out.parent.mkdir(parents=True, exist_ok=True)
doc.save(out, reproducible=True)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
"--lang",
required=True,
help="BCP-47 language tag written into the target w:lang slot.",
)
parser.add_argument(
"--slot",
required=True,
help=f"w:lang slot local-name; one of {sorted(_VALID_SLOTS)}.",
)
parser.add_argument(
"--out",
required=True,
help="Repo-relative or absolute output path for the .docx.",
)
args = parser.parse_args()
out_path = Path(args.out)
if not out_path.is_absolute():
out_path = _REPO_ROOT / out_path
_write(args.lang, args.slot, out_path)
print(out_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())
{
"$schema": "../manifest.schema.json",
"id": "docx/run-language--en-us--ea",
"kind": "literal",
"title": "Run language",
"format": "docx",
"category": "text-formatting",
"summary": "Tag a run's script slot (ASCII/Latin or East-Asian) with a language code so spell-check and hyphenation can pick the right dictionary.",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "17.3.2.20",
"element": "w:lang",
"rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
"notes": "<w:lang/> on a run's rPr has three per-script attributes: w:val (Latin/default BCP-47 language tag, stored in slot name 'val'), w:eastAsia (CJK), and w:bidi (complex-script / RTL). This family pairs five language tags with two script slots (ASCII/Latin and East-Asian) yielding 10 fixtures. python-docx has no first-class API for w:lang, so the generator writes the element via OxmlElement and sets only the targeted slot attribute."
},
"fixtures": {
"machine": "docx/run-language--en-us--ea"
},
"generator": {
"python": "scripts/gen_run_language.py",
"arg_template": "--lang {lang.val} --slot {slot.attr} --out fixtures/docx/run-language--{lang.id}--{slot.id}.docx"
},
"_expansion": {
"parent_id": "docx/run-language",
"bindings": {
"lang": "en-us",
"slot": "ea"
}
},
"assertions": [
{
"id": "lang-element-present-en-us-ea",
"part": "word/document.xml",
"namespaces": {
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
},
"xpath": "//w:r[w:t[normalize-space()='Lang tagged text']]/w:rPr/w:lang",
"must": "exist",
"description": "The run must carry a <w:lang> child inside <w:rPr>."
},
{
"id": "lang-ea-is-en-us",
"part": "word/document.xml",
"namespaces": {
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
},
"xpath": "//w:r[w:t[normalize-space()='Lang tagged text']]/w:rPr/w:lang/@w:eastAsia",
"must": "equal",
"value": "en-US",
"description": "The selected w:lang slot attribute must carry the expected BCP-47 language tag."
}
],
"render_assertions": [
{
"id": "lang-attribute-propagated-en-us-ea",
"kind": "css_selector",
"selector": "html[lang], .docx-wrapper[lang], .docx-wrapper [lang], .docx-wrapper [xml\\:lang]",
"must": "exist",
"description": "Accessibility: the w:lang tag on the run should surface somewhere in the rendered DOM — as a lang attribute on the root html, on the .docx-wrapper, on any descendant element (paragraph or run), or as an xml:lang attribute. Screen readers switch pronunciation on this hint; a renderer that drops it entirely fails this assertion."
}
]
}
