or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access-control.mdconfiguration.mddjango-signals.mdfile-operations.mdindex.mdstorage-backends.mdtemplate-integration.mdtranslation-services.mdweb-interface.md

file-operations.mddocs/

0

# File Operations and Utilities

1

2

Utilities for discovering, processing, and managing .po/.mo translation files across Django projects. These functions handle the core file system operations needed for translation management, including file discovery, timestamp handling, and pagination utilities.

3

4

## Capabilities

5

6

### Translation File Discovery

7

8

Core function for locating .po translation files across different types of Django applications.

9

10

```python { .api }

11

def find_pos(lang: str, project_apps: bool = True, django_apps: bool = False, third_party_apps: bool = False) -> list:

12

"""

13

Find .po files for specified language across application types.

14

15

Searches for translation files in different categories of Django applications

16

based on the provided parameters. Returns list of file paths with metadata.

17

18

Parameters:

19

- lang: Language code to search for (e.g., 'en', 'fr', 'de')

20

- project_apps: Include project-specific applications (default: True)

21

- django_apps: Include Django framework applications (default: False)

22

- third_party_apps: Include third-party applications (default: False)

23

24

Returns:

25

List of dictionaries containing:

26

- 'po_path': Full path to .po file

27

- 'mo_path': Full path to corresponding .mo file

28

- 'app_name': Application name

29

- 'domain': Translation domain (e.g., 'django', 'djangojs')

30

- 'is_writable': Boolean indicating write permissions

31

- 'stats': Statistics dict with 'translated', 'untranslated', 'fuzzy' counts

32

33

Respects:

34

- ROSETTA_EXCLUDED_APPLICATIONS setting

35

- ROSETTA_EXCLUDED_PATHS setting

36

- ROSETTA_POFILENAMES setting for allowed filenames

37

"""

38

```

39

40

### Timestamp Utilities

41

42

Function for generating properly formatted timestamps with timezone information.

43

44

```python { .api }

45

def timestamp_with_timezone(dt=None) -> str:

46

"""

47

Generate timestamp with timezone information.

48

49

Creates timestamp string suitable for .po file headers and logging,

50

following GNU gettext conventions for date formatting.

51

52

Parameters:

53

- dt: datetime object to format (optional, defaults to current time)

54

55

Returns:

56

Formatted timestamp string with timezone (e.g., "2025-01-15 10:30 +0000")

57

58

Format follows .po file header requirements:

59

- YYYY-MM-DD HH:MM+ZZZZ format

60

- Uses system timezone if available

61

- Falls back to UTC if timezone cannot be determined

62

"""

63

```

64

65

### Pagination Utilities

66

67

Function for generating pagination ranges for the web interface.

68

69

```python { .api }

70

def pagination_range(first: int, last: int, current: int) -> list:

71

"""

72

Generate pagination ranges for UI display.

73

74

Creates smart pagination ranges that show relevant page numbers

75

around the current page, with ellipsis for gaps.

76

77

Parameters:

78

- first: First page number (typically 1)

79

- last: Last page number

80

- current: Current page number

81

82

Returns:

83

List of page numbers and/or ellipsis strings for display

84

85

Examples:

86

- pagination_range(1, 10, 1) -> [1, 2, 3, '...', 10]

87

- pagination_range(1, 10, 5) -> [1, '...', 4, 5, 6, '...', 10]

88

- pagination_range(1, 5, 3) -> [1, 2, 3, 4, 5]

89

"""

90

```

91

92

### Cache Instance

93

94

Cache instance used for file operation caching and optimization.

95

96

```python { .api }

97

cache

98

"""

99

Django cache instance for Rosetta file operations.

100

101

Used for:

102

- Caching file discovery results

103

- Storing .po file statistics

104

- Optimizing repeated file system access

105

- Temporary storage of file metadata

106

107

Cache key patterns:

108

- 'rosetta_file_list_{lang}_{hash}': File discovery results

109

- 'rosetta_po_stats_{path_hash}': .po file statistics

110

- 'rosetta_app_list': Application discovery results

111

"""

112

```

113

114

## Usage Examples

115

116

### Basic File Discovery

117

118

```python

119

from rosetta.poutil import find_pos

120

121

def discover_translation_files():

122

"""Discover all available translation files."""

123

124

# Find French translation files in project applications only

125

french_files = find_pos('fr', project_apps=True, django_apps=False, third_party_apps=False)

126

127

for file_info in french_files:

128

print(f"App: {file_info['app_name']}")

129

print(f"Domain: {file_info['domain']}")

130

print(f"PO file: {file_info['po_path']}")

131

print(f"Writable: {file_info['is_writable']}")

132

print(f"Stats: {file_info['stats']}")

133

print("---")

134

135

# Find all translation files including Django and third-party apps

136

all_files = find_pos('es', project_apps=True, django_apps=True, third_party_apps=True)

137

138

return all_files

139

```

140

141

### File Statistics Analysis

142

143

```python

144

from rosetta.poutil import find_pos

145

146

def analyze_translation_progress(language):

147

"""Analyze translation progress for a language."""

148

149

files = find_pos(language)

150

151

total_stats = {

152

'translated': 0,

153

'untranslated': 0,

154

'fuzzy': 0

155

}

156

157

app_stats = {}

158

159

for file_info in files:

160

app_name = file_info['app_name']

161

stats = file_info['stats']

162

163

# Aggregate totals

164

for key in total_stats:

165

total_stats[key] += stats.get(key, 0)

166

167

# Per-app statistics

168

if app_name not in app_stats:

169

app_stats[app_name] = {'translated': 0, 'untranslated': 0, 'fuzzy': 0}

170

171

for key in app_stats[app_name]:

172

app_stats[app_name][key] += stats.get(key, 0)

173

174

# Calculate percentages

175

total_entries = sum(total_stats.values())

176

if total_entries > 0:

177

completion_percentage = (total_stats['translated'] / total_entries) * 100

178

else:

179

completion_percentage = 0

180

181

return {

182

'language': language,

183

'total_stats': total_stats,

184

'app_stats': app_stats,

185

'completion_percentage': completion_percentage

186

}

187

```

188

189

### Custom File Filtering

190

191

```python

192

from rosetta.poutil import find_pos

193

194

def find_writable_files(language):

195

"""Find only writable translation files for editing."""

196

197

all_files = find_pos(language, project_apps=True, django_apps=True)

198

199

# Filter for writable files only

200

writable_files = [

201

file_info for file_info in all_files

202

if file_info['is_writable']

203

]

204

205

return writable_files

206

207

def find_incomplete_files(language, threshold=50):

208

"""Find files with translation completion below threshold."""

209

210

all_files = find_pos(language)

211

incomplete_files = []

212

213

for file_info in all_files:

214

stats = file_info['stats']

215

total = stats.get('translated', 0) + stats.get('untranslated', 0) + stats.get('fuzzy', 0)

216

217

if total > 0:

218

completion = (stats.get('translated', 0) / total) * 100

219

if completion < threshold:

220

file_info['completion_percentage'] = completion

221

incomplete_files.append(file_info)

222

223

return incomplete_files

224

```

225

226

### Timestamp Generation

227

228

```python

229

from rosetta.poutil import timestamp_with_timezone

230

from datetime import datetime, timezone

231

232

def update_po_file_header():

233

"""Generate timestamps for .po file headers."""

234

235

# Current timestamp

236

current_timestamp = timestamp_with_timezone()

237

print(f"Current: {current_timestamp}")

238

239

# Specific datetime

240

specific_time = datetime(2025, 1, 15, 10, 30, 0, tzinfo=timezone.utc)

241

specific_timestamp = timestamp_with_timezone(specific_time)

242

print(f"Specific: {specific_timestamp}")

243

244

# Use in .po file header

245

po_header = f'''# SOME DESCRIPTIVE TITLE.

246

# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER

247

# This file is distributed under the same license as the PACKAGE package.

248

# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.

249

#

250

msgid ""

251

msgstr ""

252

"Project-Id-Version: PACKAGE VERSION\\n"

253

"Report-Msgid-Bugs-To: \\n"

254

"POT-Creation-Date: {current_timestamp}\\n"

255

"PO-Revision-Date: {current_timestamp}\\n"

256

"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"

257

"Language-Team: LANGUAGE <LL@li.org>\\n"

258

"MIME-Version: 1.0\\n"

259

"Content-Type: text/plain; charset=UTF-8\\n"

260

"Content-Transfer-Encoding: 8bit\\n"

261

'''

262

263

return po_header

264

```

265

266

### Pagination Implementation

267

268

```python

269

from rosetta.poutil import pagination_range

270

271

def generate_pagination_context(current_page, total_pages):

272

"""Generate pagination context for templates."""

273

274

if total_pages <= 1:

275

return {'show_pagination': False}

276

277

# Generate page range

278

page_range = pagination_range(1, total_pages, current_page)

279

280

# Build pagination context

281

context = {

282

'show_pagination': True,

283

'current_page': current_page,

284

'total_pages': total_pages,

285

'page_range': page_range,

286

'has_previous': current_page > 1,

287

'has_next': current_page < total_pages,

288

'previous_page': current_page - 1 if current_page > 1 else None,

289

'next_page': current_page + 1 if current_page < total_pages else None,

290

}

291

292

return context

293

294

# Usage in views

295

def translation_list_view(request):

296

from django.core.paginator import Paginator

297

from rosetta.conf import settings as rosetta_settings

298

299

# Get translation entries

300

entries = get_translation_entries() # Your function to get entries

301

302

# Paginate

303

paginator = Paginator(entries, rosetta_settings.MESSAGES_PER_PAGE)

304

page_number = request.GET.get('page', 1)

305

page_obj = paginator.get_page(page_number)

306

307

# Generate pagination context

308

pagination_context = generate_pagination_context(

309

page_obj.number,

310

paginator.num_pages

311

)

312

313

return render(request, 'template.html', {

314

'page_obj': page_obj,

315

'pagination': pagination_context

316

})

317

```

318

319

### File System Monitoring

320

321

```python

322

import os

323

from rosetta.poutil import find_pos, cache

324

325

def monitor_translation_files(language):

326

"""Monitor translation files for changes."""

327

328

files = find_pos(language)

329

file_info = {}

330

331

for file_data in files:

332

po_path = file_data['po_path']

333

334

if os.path.exists(po_path):

335

stat = os.stat(po_path)

336

file_info[po_path] = {

337

'mtime': stat.st_mtime,

338

'size': stat.st_size,

339

'is_writable': os.access(po_path, os.W_OK)

340

}

341

342

# Cache file information for comparison

343

cache_key = f'rosetta_file_monitor_{language}'

344

previous_info = cache.get(cache_key, {})

345

cache.set(cache_key, file_info, 3600) # Cache for 1 hour

346

347

# Detect changes

348

changed_files = []

349

for path, info in file_info.items():

350

if path in previous_info:

351

prev_info = previous_info[path]

352

if (info['mtime'] != prev_info['mtime'] or

353

info['size'] != prev_info['size']):

354

changed_files.append(path)

355

else:

356

# New file

357

changed_files.append(path)

358

359

return changed_files

360

361

def clear_file_caches():

362

"""Clear all file-related caches."""

363

364

# Clear file discovery caches

365

cache_keys_to_clear = []

366

367

# You would need to implement cache key pattern matching

368

# This is a simplified example

369

for key in cache._cache.keys():

370

if key.startswith('rosetta_file_'):

371

cache_keys_to_clear.append(key)

372

373

for key in cache_keys_to_clear:

374

cache.delete(key)

375

```