-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfix-i18n-images.py
More file actions
278 lines (223 loc) · 9.19 KB
/
Copy pathfix-i18n-images.py
File metadata and controls
278 lines (223 loc) · 9.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#!/usr/bin/env python3
"""
Fix broken image URLs in translated documentation files.
Strategy:
- For each translated file with broken URLs, find its English counterpart
- Extract image references from both files (by line order)
- Build a mapping of broken URL → fixed replacement
- Apply replacements to the translated file
Images are not translated, so the English file's image references
are the correct replacements for all locales.
"""
import os
import re
import subprocess
import sys
from collections import defaultdict
# Paths
EN_BASE = '/home/dave/ultimate-plugins/docs/docs'
I18N_BASE = '/home/dave/ultimate-plugins/docs/i18n'
# Broken URL domains
BROKEN_DOMAINS = [
'wp-ultimo-space.fra1.cdn.digitaloceanspaces.com',
'downloads.intercomcdn.com',
'support.delta.nextpress.co',
]
# Regex to match image markdown lines with broken URLs
BROKEN_URL_RE = re.compile(
r'(?:!\[[^\]]*\]\([^)]*(?:' +
'|'.join(re.escape(d) for d in BROKEN_DOMAINS) +
r')[^)]*\))|'
r'(?:\[!\[[^\]]*\]\([^)]*(?:' +
'|'.join(re.escape(d) for d in BROKEN_DOMAINS) +
r')[^)]*\)\]\([^)]*\))'
)
# Regex to match any image reference (for English files)
IMAGE_RE = re.compile(
r'(?:!\[[^\]]*\]\([^)]*\))|' # 
r'(?:\[!\[[^\]]*\]\([^)]*\)\]\([^)]*\))|' # [](url)
r'(?:<!--\s*Screenshot unavailable:[^>]*-->)' # <!-- Screenshot unavailable: ... -->
)
# Regex to check if a line contains a broken domain
BROKEN_DOMAIN_RE = re.compile(
'|'.join(re.escape(d) for d in BROKEN_DOMAINS)
)
# Also match the FAQ's broken link (not an image but a broken URL)
BROKEN_LINK_RE = re.compile(
r'\[([^\]]*)\]\(https?://support\.delta\.nextpress\.co[^)]*\)'
)
def extract_image_lines(filepath):
"""Extract (line_number, line_content) for lines containing image references."""
images = []
with open(filepath, 'r', encoding='utf-8') as f:
for i, line in enumerate(f):
stripped = line.strip()
if not stripped:
continue
# Check for image markdown or screenshot placeholder
if IMAGE_RE.search(stripped) or BROKEN_DOMAIN_RE.search(stripped):
images.append((i, line))
return images
def extract_broken_lines(filepath):
"""Extract (line_number, line_content) for lines with broken URLs."""
broken = []
with open(filepath, 'r', encoding='utf-8') as f:
for i, line in enumerate(f):
if BROKEN_DOMAIN_RE.search(line):
broken.append((i, line))
return broken
def build_replacement_map(en_file, translated_file):
"""
Build a map of old_line → new_line by matching image positions.
Since images aren't translated, we match them by their order of appearance.
Each broken image in the translated file maps to the image at the same
position in the English file.
"""
en_images = extract_image_lines(en_file)
tr_broken = extract_broken_lines(translated_file)
if not tr_broken:
return {}
# Read translated file to get all image lines (broken + ok)
tr_images = extract_image_lines(translated_file)
# Build positional mapping: for each image position in translated file,
# find the corresponding English image
replacements = {}
# Simple approach: match by sequential position
# The nth image in translated file corresponds to the nth image in English
en_idx = 0
tr_idx = 0
while tr_idx < len(tr_images) and en_idx < len(en_images):
tr_lineno, tr_line = tr_images[tr_idx]
en_lineno, en_line = en_images[en_idx]
if BROKEN_DOMAIN_RE.search(tr_line):
replacements[tr_lineno] = en_line
tr_idx += 1
en_idx += 1
return replacements
def fix_file(translated_file, replacements):
"""Apply line replacements to a file."""
with open(translated_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
changed = 0
for lineno, new_line in replacements.items():
if lineno < len(lines):
old_line = lines[lineno]
if old_line.strip() != new_line.strip():
# Preserve original indentation
indent = len(old_line) - len(old_line.lstrip())
indent_str = old_line[:indent]
lines[lineno] = indent_str + new_line.strip() + '\n'
changed += 1
if changed > 0:
with open(translated_file, 'w', encoding='utf-8') as f:
f.writelines(lines)
return changed
def get_translated_files():
"""Find all translated files with broken URLs."""
result = subprocess.run(
['grep', '-rl', '-E',
r'wp-ultimo-space\.fra1\.cdn\.digitaloceanspaces\.com|downloads\.intercomcdn\.com|support\.delta\.nextpress\.co',
I18N_BASE],
capture_output=True, text=True
)
if result.returncode != 0:
return []
return [f.strip() for f in result.stdout.strip().split('\n') if f.strip()]
def get_relpath(filepath):
"""Extract relative doc path from a translated file path."""
parts = filepath.split('/current/', 1)
if len(parts) == 2:
return parts[1]
return None
def main():
dry_run = '--dry-run' in sys.argv
verbose = '-v' in sys.argv or '--verbose' in sys.argv
if dry_run:
print("DRY RUN - no files will be modified\n")
translated_files = get_translated_files()
print(f"Found {len(translated_files)} translated files with broken URLs\n")
# Group by relative path
by_relpath = defaultdict(list)
for f in translated_files:
rp = get_relpath(f)
if rp:
by_relpath[rp].append(f)
total_files_fixed = 0
total_replacements = 0
errors = []
for relpath in sorted(by_relpath.keys()):
en_file = os.path.join(EN_BASE, relpath)
if not os.path.exists(en_file):
errors.append(f"English file not found: {en_file}")
continue
# Check English file has no broken URLs
en_broken = extract_broken_lines(en_file)
if en_broken:
errors.append(f"English file still has {len(en_broken)} broken URLs: {relpath}")
continue
locale_files = by_relpath[relpath]
if verbose:
print(f"\n--- {relpath} ({len(locale_files)} locales) ---")
for tr_file in sorted(locale_files):
# Extract locale from path
locale = tr_file.split('/i18n/')[1].split('/')[0]
replacements = build_replacement_map(en_file, tr_file)
if not replacements:
if verbose:
print(f" [{locale}] No replacements needed (possibly mismatched structure)")
# Fallback: do simple string replacements for known patterns
# This handles cases where line structure differs
with open(tr_file, 'r', encoding='utf-8') as f:
content = f.read()
# Handle the FAQ broken link specially
if 'support.delta.nextpress.co' in content and 'frequently-asked-questions' in tr_file:
new_content = re.sub(
r'\[([^\]]*)\]\(https?://support\.delta\.nextpress\.co[^)]*\)',
r'[\1](/docs/user-guide/getting-started/how-to-install-wordpress-multisite)',
content
)
if new_content != content:
if not dry_run:
with open(tr_file, 'w', encoding='utf-8') as f:
f.write(new_content)
count = content.count('support.delta.nextpress.co') - new_content.count('support.delta.nextpress.co')
print(f" [{locale}] Fixed {count} broken link(s) (FAQ fallback)")
total_files_fixed += 1
total_replacements += count
continue
if dry_run:
print(f" [{locale}] Would replace {len(replacements)} image(s)")
total_files_fixed += 1
total_replacements += len(replacements)
if verbose:
for lineno, new_line in sorted(replacements.items()):
print(f" L{lineno+1}: → {new_line.strip()[:80]}...")
else:
changed = fix_file(tr_file, replacements)
if changed > 0:
print(f" [{locale}] Fixed {changed} image(s)")
total_files_fixed += 1
total_replacements += changed
elif verbose:
print(f" [{locale}] No changes needed")
# Summary
print(f"\n{'=' * 60}")
print(f"SUMMARY")
print(f"{'=' * 60}")
print(f"Files fixed: {total_files_fixed}")
print(f"Total replacements: {total_replacements}")
if errors:
print(f"\nErrors ({len(errors)}):")
for e in errors:
print(f" - {e}")
# Verify
if not dry_run:
remaining = get_translated_files()
print(f"\nRemaining files with broken URLs: {len(remaining)}")
if remaining and verbose:
for f in remaining[:20]:
print(f" {f}")
if len(remaining) > 20:
print(f" ... and {len(remaining) - 20} more")
if __name__ == '__main__':
main()