Skip to content

Commit 3d031d2

Browse files
Update scripts for handling pages with native labels
1 parent 5d42fb0 commit 3d031d2

7 files changed

Lines changed: 562 additions & 27 deletions

src/main/abstract/missing-content.md

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ This creates three ignored working files:
5050
src/main/abstract/missing-content-review.csv
5151
src/main/abstract/missing-content.quickstatements
5252
src/main/abstract/missing-content-partial.quickstatements
53+
src/main/abstract/missing-content-label-updates.quickstatements
5354
```
5455

5556
The generator:
@@ -62,7 +63,8 @@ The generator:
6263
separately as reusable local entities;
6364
6. resolves links to local `Q….html` pages as entity references;
6465
7. compares multilingual values with the offline Wikibase exports;
65-
8. prepares separate deduplicated imports for complete items and for partial
66+
8. forces native (English-page) labels on the proper-name list pages below;
67+
9. prepares separate deduplicated imports for complete items and for partial
6668
items that currently have at least English and French.
6769

6870
Complete items can move through the eight-language review immediately.
@@ -74,6 +76,33 @@ expose alignment errors.
7476
Never edit `missing-content.quickstatements` as the source of truth. Correct
7577
translations in the localized pages or review data, then regenerate it.
7678

79+
### Native-label list pages (never translated)
80+
81+
Some abstract pages are curated lists of proper names — film, book, series and
82+
museum titles, and verbatim quotes — whose English page already carries the
83+
authoritative label for every entry (for example, a French film keeps its
84+
French title on the English page). These titles must **not** be translated:
85+
the English value is always the correct one. The generator recognises these
86+
pages by their English source path and reuses the English value verbatim for
87+
every language, so both the `P40` content values and the finalized labels stay
88+
native and no per-language translation or `P40` replacement is emitted. Rows
89+
from these pages are marked with `native_labels = 1` in the review CSV.
90+
91+
The current native-label pages (`NATIVE_LABEL_SOURCES` in
92+
`prepare_missing_content.py`) are:
93+
94+
```text
95+
en/writings/books-i-read.html (Books I Read)
96+
en/writings/films-series-documentaries.html (Films, Series and Documentaries I watched)
97+
en/writings/music.html (Music I Listen)
98+
en/writings/museums-galleries.html (Museums and Galleries I visited)
99+
en/writings/quotes.html (Quotes)
100+
```
101+
102+
To add another such page, append its English source path to that set. Do not
103+
translate these entries in the localized pages or review data; the English
104+
page is the single source of truth for their labels.
105+
77106
## 2. Review the inventory
78107

79108
`missing-content-review.csv` identifies a DOM slot with:
@@ -82,8 +111,10 @@ translations in the localized pages or review data, then regenerate it.
82111
page, path, tag, class, role, occurrence
83112
```
84113

85-
It also contains `status`, `qid`, candidate QIDs, an import `token`, the
86-
canonical text, and the eight localized values.
114+
It also contains `native_labels` (`1` for proper-name list pages whose labels
115+
are never translated), `status`, `qid`, candidate QIDs, an import `token`, the
116+
canonical text, and the eight localized values. For `native_labels` rows the
117+
eight values are all identical to the English label by design.
87118

88119
Interpret statuses as follows:
89120

@@ -115,7 +146,9 @@ the local Wikibase instance.
115146
`missing-content-partial.quickstatements` is a separate import batch. Import it
116147
only after verifying each available value against its page context. Missing
117148
languages are deliberately absent from `P40`; never insert an English fallback
118-
as though it were a translation.
149+
as though it were a translation. The one exception is the native-label list
150+
pages above: there the English value is the genuine native label for every
151+
language, not a fallback, so it is reused verbatim by design.
119152

120153
Machine-assisted values for languages such as Malayalam, Punjabi, and Hindi
121154
must be held for human review before they are merged into an eight-language
@@ -137,6 +170,12 @@ Record the QID returned for every `M…` token. In
137170
carrying the token. Also fill reviewed QIDs for ambiguous rows. A blank `qid`
138171
means "do not bind".
139172

173+
For large batches, refresh `labels-wikibase.csv` instead of copying QIDs by
174+
hand and rerun the generator. It reconciles the temporary
175+
`M… abstract content` English labels automatically and writes
176+
`missing-content-label-updates.quickstatements`. Import that batch to replace
177+
the temporary labels with the reviewed labels in all eight languages.
178+
140179
## 4. Bind reviewed QIDs
141180

142181
Preview whether binding work remains:

src/main/abstract/pipe_to_tab.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/env python3
2+
"""Fallback converter: pipe-separated QuickStatements -> TAB-separated.
3+
4+
The generators in this package emit pipe-separated QuickStatements, which is the
5+
preferred format. QuickStatements accepts ``TAB`` *or* ``|`` as the field
6+
separator, but it has no ``\\|`` escape, so a value that itself contains a
7+
literal ``|`` (for example an HTML ``<title>`` such as ``"Model | Project 3D"``)
8+
cannot be represented while ``|`` is also the separator. When a batch contains
9+
such values, run it through this converter to obtain an equivalent TAB-separated
10+
file that imports correctly.
11+
12+
This is a fallback only; the primary output stays pipe-separated. The converter
13+
rewrites separator pipes (those outside quoted strings) as TABs, restores
14+
``\\|`` to a literal ``|``, and converts backslash-escaped quotes to the
15+
QuickStatements ``""`` form.
16+
"""
17+
18+
from __future__ import annotations
19+
20+
import argparse
21+
import sys
22+
from pathlib import Path
23+
24+
25+
def pipe_to_tab_line(line: str) -> str:
26+
"""Convert one pipe-separated QuickStatements line to TAB-separated.
27+
28+
Pipes outside quoted strings are separators and become TABs. Inside a quoted
29+
string, ``\\|`` and ``\\\\`` are unescaped to their literal character and
30+
``\\"`` becomes ``""`` (QuickStatements escapes quotes by doubling them).
31+
"""
32+
out: list[str] = []
33+
in_quote = False
34+
index = 0
35+
length = len(line)
36+
while index < length:
37+
character = line[index]
38+
if not in_quote:
39+
if character == "|":
40+
out.append("\t")
41+
elif character == '"':
42+
in_quote = True
43+
out.append('"')
44+
else:
45+
out.append(character)
46+
index += 1
47+
elif character == "\\" and index + 1 < length:
48+
following = line[index + 1]
49+
out.append('""' if following == '"' else following)
50+
index += 2
51+
elif character == '"':
52+
in_quote = False
53+
out.append('"')
54+
index += 1
55+
else:
56+
out.append(character)
57+
index += 1
58+
return "".join(out)
59+
60+
61+
def pipe_to_tab(text: str) -> str:
62+
"""Convert pipe-separated QuickStatements text to TAB-separated text."""
63+
return "\n".join(pipe_to_tab_line(line) for line in text.splitlines())
64+
65+
66+
def convert_file(source: Path, destination: Path) -> None:
67+
converted = pipe_to_tab(source.read_text(encoding="utf-8"))
68+
destination.write_text(converted + "\n", encoding="utf-8")
69+
70+
71+
def main() -> int:
72+
parser = argparse.ArgumentParser(description=__doc__)
73+
parser.add_argument("source", type=Path, help="pipe-separated .quickstatements file")
74+
parser.add_argument(
75+
"-o",
76+
"--output",
77+
type=Path,
78+
default=None,
79+
help="destination file (default: <source stem>.tab.quickstatements)",
80+
)
81+
args = parser.parse_args()
82+
destination = args.output or args.source.with_suffix(".tab.quickstatements")
83+
try:
84+
convert_file(args.source, destination)
85+
except OSError as error:
86+
print(f"ERROR: {error}", file=sys.stderr)
87+
return 1
88+
print(f"Wrote {destination}")
89+
return 0
90+
91+
92+
if __name__ == "__main__":
93+
raise SystemExit(main())

0 commit comments

Comments
 (0)