Skip to content

Commit 8f43a3b

Browse files
authored
Merge pull request #555 from lbedner/mandarin-support-add-update
Mandarin support add update
2 parents e539dc5 + 10added commit 8f43a3b

File tree

22 files changed

+1035
-335
lines changed

22 files changed

+1035
-335
lines changed

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Each generated project includes:
3232

3333
## Installation
3434

35-
**Current Version**: 0.6.5
35+
**Current Version**: 0.6.6rc1
3636

3737
```bash
3838
pip install aegis-stack

aegis/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Aegis Stack CLI - Component generation and project management tools.
33
"""
44

5-
__version__ = "0.6.5"
5+
__version__ = "0.6.6rc1"

aegis/cli/callbacks.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from ..core.dependency_resolver import DependencyResolver
2424
from ..core.service_resolver import ServiceResolver
2525
from ..core.services import SERVICES
26+
from ..i18n import t
2627
from .interactive import set_ai_service_config, set_auth_level_selection
2728
from .utils import expand_scheduler_dependencies
2829

@@ -165,7 +166,9 @@ def validate_and_resolve_services(
165166
]
166167
if unknown_services:
167168
typer.secho(
168-
f"Unknown services: {', '.join(unknown_services)}", fg="red", err=True
169+
t("validation.unknown_services", names=", ".join(unknown_services)),
170+
fg="red",
171+
err=True,
169172
)
170173
available = list(SERVICES.keys())
171174
typer.echo(f"Available services: {', '.join(available)}", err=True)

aegis/commands/add.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ def add_command(
180180
if auto_added:
181181
typer.echo(t("add.auto_added_deps", deps=", ".join(auto_added)))
182182

183+
except typer.Exit:
184+
raise
183185
except Exception as e:
184186
typer.secho(t("add.validation_failed", error=e), fg="red", err=True)
185187
raise typer.Exit(1)

aegis/commands/add_service.py

Lines changed: 98 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@
3131
from ..core.project_map import render_project_map
3232
from ..core.service_resolver import ServiceResolver
3333
from ..core.services import SERVICES, get_service_dependencies
34+
from ..i18n import t
35+
36+
37+
def _translated_component_desc(name: str, fallback: str) -> str:
38+
"""Get translated description for a component, with fallback."""
39+
key = f"component.{name}"
40+
result = t(key)
41+
return result if result != key else fallback
42+
43+
44+
def _translated_service_desc(name: str, fallback: str) -> str:
45+
"""Get translated description for a service, with fallback."""
46+
key = f"service.{name}"
47+
result = t(key)
48+
return result if result != key else fallback
3449

3550

3651
def add_service_command(
@@ -70,7 +85,7 @@ def add_service_command(
7085
(the default since v0.2.0). Services may auto-add required components.
7186
"""
7287

73-
typer.secho("Aegis Stack - Add Services", fg=typer.colors.BLUE, bold=True)
88+
typer.echo(t("add_service.title"))
7489
typer.echo("=" * 50)
7590

7691
# Resolve project path
@@ -79,33 +94,26 @@ def add_service_command(
7994
# Validate it's a Copier project
8095
validate_copier_project(target_path, "add-service")
8196

82-
typer.echo(f"{typer.style('Project:', fg=typer.colors.CYAN)} {target_path}")
97+
typer.echo(t("add_service.project", path=target_path))
8398

8499
# Validate services argument or interactive mode
85100
if not interactive and not services:
86-
typer.secho(
87-
"Error: services argument is required (or use --interactive)",
88-
fg="red",
89-
err=True,
90-
)
91-
typer.echo(" Usage: aegis add service auth,ai", err=True)
92-
typer.echo(" Or: aegis add service --interactive", err=True)
101+
typer.secho(t("add_service.error_no_args"), fg="red", err=True)
102+
typer.echo(f" {t('add_service.usage_hint')}", err=True)
103+
typer.echo(f" {t('add_service.interactive_hint')}", err=True)
93104
raise typer.Exit(1)
94105

95106
# Interactive mode
96107
if interactive:
97108
if services:
98-
typer.secho(
99-
"Warning: --interactive flag ignores service arguments",
100-
fg="yellow",
101-
)
109+
typer.secho(t("add_service.interactive_ignores_args"), fg="yellow")
102110

103111
from ..cli.interactive import interactive_service_selection
104112

105113
selected_services = interactive_service_selection(target_path)
106114

107115
if not selected_services:
108-
typer.secho("\nNo services selected", fg="green")
116+
typer.secho(f"\n{t('add_service.no_selected')}", fg="green")
109117
raise typer.Exit(0)
110118

111119
# Convert to comma-separated string for existing logic
@@ -134,15 +142,17 @@ def add_service_command(
134142
for error in errors:
135143
typer.secho(f"{error}", fg="red", err=True)
136144
raise typer.Exit(1)
145+
except typer.Exit:
146+
raise
137147
except Exception as e:
138-
typer.secho(f"Service validation failed: {e}", fg="red", err=True)
148+
typer.secho(t("add_service.validation_failed", error=e), fg="red", err=True)
139149
raise typer.Exit(1)
140150

141151
# Load existing project configuration
142152
try:
143153
existing_answers = load_copier_answers(target_path)
144154
except Exception as e:
145-
typer.secho(f"Failed to load project configuration: {e}", fg="red", err=True)
155+
typer.secho(t("add_service.load_config_failed", error=e), fg="red", err=True)
146156
raise typer.Exit(1)
147157

148158
# Check which services are already enabled
@@ -155,13 +165,15 @@ def add_service_command(
155165
already_enabled.append(service)
156166

157167
if already_enabled:
158-
typer.echo(f"Already enabled: {', '.join(already_enabled)}", err=False)
168+
typer.echo(
169+
t("add_service.already_enabled", services=", ".join(already_enabled))
170+
)
159171

160172
# Filter out already enabled services
161173
services_to_add = [s for s in selected_services if s not in already_enabled]
162174

163175
if not services_to_add:
164-
typer.secho("All requested services are already enabled!", fg="green")
176+
typer.secho(t("add_service.all_enabled"), fg="green")
165177
raise typer.Exit(0)
166178

167179
# Handle AI service interactive configuration
@@ -214,7 +226,7 @@ def add_service_command(
214226
services_to_add
215227
)
216228
except ValueError as e:
217-
typer.secho(f"Failed to resolve service dependencies: {e}", fg="red", err=True)
229+
typer.secho(t("add_service.resolve_failed", error=e), fg="red", err=True)
218230
raise typer.Exit(1)
219231

220232
# If AI service selected SQLite backend, ensure database is in required components
@@ -236,36 +248,40 @@ def add_service_command(
236248
missing_components.append(component)
237249

238250
# Show what will be added
239-
typer.secho("\nServices to add:", fg=typer.colors.CYAN, bold=True)
251+
typer.secho(
252+
f"\n{t('add_service.services_to_add')}", fg=typer.colors.CYAN, bold=True
253+
)
240254
for service in services_to_add:
241255
base_service = service_base_map[service]
242256
if base_service in SERVICES:
243-
desc = SERVICES[base_service].description
257+
desc = _translated_service_desc(
258+
base_service, SERVICES[base_service].description
259+
)
244260
typer.echo(f" • {service}: {desc}")
245261

246262
# Show component requirements
247263
if missing_components:
248-
typer.secho(
249-
"\nRequired components (will be auto-added):", fg=typer.colors.YELLOW
250-
)
264+
typer.secho(f"\n{t('add_service.required_components')}", fg=typer.colors.YELLOW)
251265
for component in missing_components:
252266
if component in COMPONENTS:
253-
desc = COMPONENTS[component].description
267+
desc = _translated_component_desc(
268+
component, COMPONENTS[component].description
269+
)
254270
typer.echo(f" • {component}: {desc}")
255271

256272
if enabled_components:
257273
# Filter out core components from display
258274
non_core_enabled = [c for c in enabled_components if c not in CORE_COMPONENTS]
259275
if non_core_enabled:
260276
typer.secho(
261-
f"\nAlready have required components: {', '.join(non_core_enabled)}",
277+
f"\n{t('add_service.already_have_components', components=', '.join(non_core_enabled))}",
262278
fg="green",
263279
)
264280

265281
# Confirm before proceeding
266282
typer.echo()
267-
if not yes and not typer.confirm("Add these services?", default=True):
268-
typer.secho("Operation cancelled", fg="red")
283+
if not yes and not typer.confirm(t("add_service.confirm"), default=True):
284+
typer.secho(t("shared.operation_cancelled"), fg="red")
269285
raise typer.Exit(0)
270286

271287
# Prepare update data for ManualUpdater
@@ -284,14 +300,14 @@ def add_service_command(
284300
update_data[include_key] = True
285301

286302
# Add services using ManualUpdater
287-
typer.secho("\nUpdating project...", fg=typer.colors.CYAN, bold=True)
288303
try:
289304
updater = ManualUpdater(target_path)
290305

291306
# Add missing components first
292307
for component in missing_components:
293308
typer.secho(
294-
f"\nAdding required component: {component}...", fg=typer.colors.CYAN
309+
f"\n{t('add_service.adding_component', component=component)}",
310+
fg=typer.colors.CYAN,
295311
)
296312

297313
# Prepare component-specific data
@@ -312,23 +328,33 @@ def add_service_command(
312328

313329
if not result.success:
314330
typer.secho(
315-
f"Failed to add component {component}: {result.error_message}",
331+
t(
332+
"add_service.failed_component",
333+
component=component,
334+
error=result.error_message,
335+
),
316336
fg="red",
317337
err=True,
318338
)
319339
raise typer.Exit(1)
320340

321341
if result.files_modified:
322-
typer.secho(f" Added {len(result.files_modified)} files", fg="green")
342+
typer.secho(
343+
f" {t('add_service.added_files', count=len(result.files_modified))}",
344+
fg="green",
345+
)
323346
if result.files_skipped:
324347
typer.secho(
325-
f" Skipped {len(result.files_skipped)} existing files",
348+
f" {t('add_service.skipped_files', count=len(result.files_skipped))}",
326349
fg="yellow",
327350
)
328351

329352
# Now add each service sequentially
330353
for service in services_to_add:
331-
typer.secho(f"\nAdding service: {service}...", fg=typer.colors.CYAN)
354+
typer.secho(
355+
f"\n{t('add_service.adding_service', service=service)}",
356+
fg=typer.colors.CYAN,
357+
)
332358

333359
# Prepare service-specific data
334360
service_data: dict[str, bool | str] = {}
@@ -361,18 +387,25 @@ def add_service_command(
361387

362388
if not result.success:
363389
typer.secho(
364-
f"Failed to add service {service}: {result.error_message}",
390+
t(
391+
"add_service.failed_service",
392+
service=service,
393+
error=result.error_message,
394+
),
365395
fg="red",
366396
err=True,
367397
)
368398
raise typer.Exit(1)
369399

370400
# Show results
371401
if result.files_modified:
372-
typer.secho(f" Added {len(result.files_modified)} files", fg="green")
402+
typer.secho(
403+
f" {t('add_service.added_files', count=len(result.files_modified))}",
404+
fg="green",
405+
)
373406
if result.files_skipped:
374407
typer.secho(
375-
f" Skipped {len(result.files_skipped)} existing files",
408+
f" {t('add_service.skipped_files', count=len(result.files_skipped))}",
376409
fg="yellow",
377410
)
378411

@@ -391,19 +424,21 @@ def add_service_command(
391424
alembic_dir = target_path / "alembic"
392425
if not alembic_dir.exists():
393426
typer.secho(
394-
"\nBootstrapping alembic infrastructure...", fg=typer.colors.CYAN
427+
f"\n{t('add_service.bootstrap_alembic')}",
428+
fg=typer.colors.CYAN,
395429
)
396430
created = bootstrap_alembic(
397431
target_path, updater.jinja_env, updater.answers
398432
)
399433
for f in created:
400-
typer.echo(f" Created: {f}")
434+
typer.echo(f" {t('add_service.created_file', file=f)}")
401435

402436
if not service_has_migration(target_path, base_service):
403437
migration_path = generate_migration(target_path, base_service)
404438
if migration_path:
405439
typer.secho(
406-
f" Generated migration: {migration_path.name}", fg="green"
440+
f" {t('add_service.generated_migration', name=migration_path.name)}",
441+
fg="green",
407442
)
408443

409444
# Auto-run migrations for services that need them
@@ -418,18 +453,17 @@ def add_service_command(
418453
and (service_base_map[s] != AnswerKeys.SERVICE_AI or ai_needs_migrations)
419454
]
420455
if services_with_migrations:
421-
typer.secho("\nApplying database migrations...", fg=typer.colors.CYAN)
456+
typer.secho(
457+
f"\n{t('add_service.applying_migrations')}", fg=typer.colors.CYAN
458+
)
422459
from ..core.post_gen_tasks import run_migrations
423460

424461
migration_success = run_migrations(target_path, include_migrations=True)
425462

426463
if not migration_success:
427-
typer.secho(
428-
"Warning: Auto-migration failed. Run 'make migrate' manually.",
429-
fg="yellow",
430-
)
464+
typer.secho(t("add_service.migration_failed"), fg="yellow")
431465

432-
typer.secho("\nServices added successfully!", fg="green")
466+
typer.secho(f"\n{t('add_service.success')}", fg="green")
433467

434468
# Show project map with newly added services + auto-added components highlighted
435469
base_services_for_highlight = [service_base_map[s] for s in services_to_add]
@@ -463,24 +497,34 @@ def add_service_command(
463497

464498
if AnswerKeys.SERVICE_AUTH in base_services_added:
465499
project_slug = existing_answers.get(AnswerKeys.PROJECT_SLUG, "my-project")
466-
typer.secho("\nAuth Service Setup:", fg=typer.colors.CYAN, bold=True)
500+
typer.secho(
501+
f"\n{t('add_service.auth_setup')}", fg=typer.colors.CYAN, bold=True
502+
)
467503
cmd = typer.style(f"{project_slug} auth create-test-users", bold=True)
468-
typer.echo(f" 1. Create test users: {cmd}")
504+
typer.echo(t("add_service.auth_create_users", cmd=cmd))
469505
url = typer.style("http://localhost:8000/docs", bold=True)
470-
typer.echo(f" 2. View auth routes: {url}")
506+
typer.echo(t("add_service.auth_view_routes", url=url))
471507

472508
if AnswerKeys.SERVICE_AI in base_services_added:
473509
project_slug = existing_answers.get(AnswerKeys.PROJECT_SLUG, "my-project")
474-
typer.secho("\nAI Service Setup:", fg=typer.colors.CYAN, bold=True)
510+
typer.secho(
511+
f"\n{t('add_service.ai_setup')}", fg=typer.colors.CYAN, bold=True
512+
)
475513
typer.echo(
476-
f" 1. Set {typer.style('AI_PROVIDER', bold=True)} in .env (openai, anthropic, google, groq)"
514+
t(
515+
"add_service.ai_set_provider",
516+
env_var=typer.style("AI_PROVIDER", bold=True),
517+
)
477518
)
478519
typer.echo(
479-
f" 2. Set provider API key ({typer.style('OPENAI_API_KEY', bold=True)}, etc.)"
520+
t(
521+
"add_service.ai_set_api_key",
522+
env_var=typer.style("OPENAI_API_KEY", bold=True),
523+
)
480524
)
481525
cmd = typer.style(f"{project_slug} ai chat", bold=True)
482-
typer.echo(f" 3. Test with CLI: {cmd}")
526+
typer.echo(t("add_service.ai_test_cli", cmd=cmd))
483527

484528
except Exception as e:
485-
typer.secho(f"\nFailed to add services: {e}", fg="red", err=True)
529+
typer.secho(f"\n{t('add_service.failed', error=e)}", fg="red", err=True)
486530
raise typer.Exit(1)

0 commit comments

Comments
 (0)