pz-cli 5.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pz_cli-5.0.0.dist-info/METADATA +51 -0
- pz_cli-5.0.0.dist-info/RECORD +399 -0
- pz_cli-5.0.0.dist-info/WHEEL +4 -0
- pz_cli-5.0.0.dist-info/entry_points.txt +5 -0
- pz_core/__init__.py +64 -0
- pz_core/__main__.py +7 -0
- pz_core/app.py +45 -0
- pz_core/assembly/__init__.py +17 -0
- pz_core/assembly/app_discovery.py +1240 -0
- pz_core/assembly/apps/__init__.py +25 -0
- pz_core/assembly/apps/compiler.py +824 -0
- pz_core/assembly/apps/providers.py +188 -0
- pz_core/assembly/cli/__init__.py +9 -0
- pz_core/assembly/cli/compiler.py +166 -0
- pz_core/assembly/diagnostics/__init__.py +21 -0
- pz_core/assembly/diagnostics/checks.py +257 -0
- pz_core/assembly/diagnostics/dev_sources.py +569 -0
- pz_core/assembly/events/__init__.py +5 -0
- pz_core/assembly/events/compiler.py +219 -0
- pz_core/assembly/extensions/__init__.py +5 -0
- pz_core/assembly/extensions/compiler.py +696 -0
- pz_core/assembly/infra/__init__.py +6 -0
- pz_core/assembly/infra/compiler.py +272 -0
- pz_core/assembly/lifecycle/__init__.py +5 -0
- pz_core/assembly/lifecycle/compiler.py +95 -0
- pz_core/assembly/middlewares/__init__.py +19 -0
- pz_core/assembly/middlewares/compiler.py +508 -0
- pz_core/assembly/project/__init__.py +7 -0
- pz_core/assembly/project/compiler.py +9 -0
- pz_core/assembly/project/context.py +17 -0
- pz_core/assembly/project/discovery.py +227 -0
- pz_core/assembly/resources/__init__.py +19 -0
- pz_core/assembly/resources/compiler.py +310 -0
- pz_core/assembly/resources/settings.py +129 -0
- pz_core/assembly/routes/__init__.py +7 -0
- pz_core/assembly/routes/compiler.py +400 -0
- pz_core/assembly/runtime_manifests/__init__.py +13 -0
- pz_core/assembly/runtime_manifests/compiler.py +159 -0
- pz_core/assembly/settings/__init__.py +17 -0
- pz_core/assembly/settings/compiler.py +786 -0
- pz_core/assembly/settings/host_project_overrides.py +82 -0
- pz_core/assembly/settings/host_runtime_endpoint.py +207 -0
- pz_core/authoring/__init__.py +79 -0
- pz_core/authoring/app_init_service.py +946 -0
- pz_core/authoring/app_versions.py +814 -0
- pz_core/authoring/cli.py +192 -0
- pz_core/authoring/commands.py +1810 -0
- pz_core/authoring/conflicts.py +180 -0
- pz_core/authoring/dev_bindings.py +916 -0
- pz_core/authoring/dev_git.py +1087 -0
- pz_core/authoring/dev_import.py +1061 -0
- pz_core/authoring/dev_manifest.py +522 -0
- pz_core/authoring/dev_sources.py +1638 -0
- pz_core/authoring/env_cli.py +454 -0
- pz_core/authoring/files.py +109 -0
- pz_core/authoring/ide_projection.py +1129 -0
- pz_core/authoring/naming.py +58 -0
- pz_core/authoring/official_dev_sources.py +432 -0
- pz_core/authoring/official_sources.json +156 -0
- pz_core/authoring/orchestration_service.py +558 -0
- pz_core/authoring/project_default.jsonc +53 -0
- pz_core/authoring/project_defaults.py +88 -0
- pz_core/authoring/project_metadata_service.py +199 -0
- pz_core/authoring/project_settings_checks.py +51 -0
- pz_core/authoring/project_settings_cli.py +299 -0
- pz_core/authoring/project_settings_service.py +1700 -0
- pz_core/authoring/project_state.py +399 -0
- pz_core/authoring/publish_git.py +423 -0
- pz_core/authoring/publish_lock.py +188 -0
- pz_core/authoring/publish_source.py +195 -0
- pz_core/authoring/release.py +1382 -0
- pz_core/authoring/repo_keys.py +29 -0
- pz_core/authoring/root_cli.py +28 -0
- pz_core/authoring/scaffold_service.py +325 -0
- pz_core/authoring/sync_service.py +787 -0
- pz_core/authoring/templates/app/__init__.py +1 -0
- pz_core/authoring/templates/app/api.py +25 -0
- pz_core/authoring/templates/app/apps.py +31 -0
- pz_core/authoring/templates/app/auth_package_init.py +14 -0
- pz_core/authoring/templates/app/auth_readme.py +29 -0
- pz_core/authoring/templates/app/configs_package_init.py +14 -0
- pz_core/authoring/templates/app/default_env.py +18 -0
- pz_core/authoring/templates/app/default_jsonc.py +25 -0
- pz_core/authoring/templates/app/locales_readme.py +25 -0
- pz_core/authoring/templates/app/migrations_readme.py +19 -0
- pz_core/authoring/templates/app/models.py +14 -0
- pz_core/authoring/templates/app/package_init.py +8 -0
- pz_core/authoring/templates/app/pages_base.py +161 -0
- pz_core/authoring/templates/app/pages_readme.py +25 -0
- pz_core/authoring/templates/app/public.py +31 -0
- pz_core/authoring/templates/app/pyproject.py +77 -0
- pz_core/authoring/templates/app/readme.py +74 -0
- pz_core/authoring/templates/app/settings.py +46 -0
- pz_core/authoring/templates/app/static_app_js.py +24 -0
- pz_core/authoring/templates/app/static_readme.py +31 -0
- pz_core/authoring/templates/app/template_config_page.py +23 -0
- pz_core/authoring/templates/app/template_index_html.py +28 -0
- pz_core/authoring/templates/app/templates_readme.py +32 -0
- pz_core/authoring/templates/app/urls.py +36 -0
- pz_core/authoring/templates/app/views.py +36 -0
- pz_core/authoring/templates/project/__init__.py +1 -0
- pz_core/authoring/templates/project/agents.py +51 -0
- pz_core/authoring/templates/project/apps_container_readme.py +37 -0
- pz_core/authoring/templates/project/main_py.py +59 -0
- pz_core/authoring/templates/project/project_env.py +19 -0
- pz_core/authoring/templates/project/pyproject.py +26 -0
- pz_core/authoring/templates/project/pz_env_env.py +65 -0
- pz_core/authoring/templates/project/pz_env_jsonc.py +69 -0
- pz_core/authoring/templates/project/pz_jsonc.py +12 -0
- pz_core/authoring/templates/project/readme.py +94 -0
- pz_core/authoring/templates/project/root_gitkeep.py +8 -0
- pz_core/authoring/templates/shared/__init__.py +5 -0
- pz_core/authoring/templates/shared/text.py +10 -0
- pz_core/authoring/venv_service.py +87 -0
- pz_core/authoring/workspace.py +167 -0
- pz_core/cli.py +291 -0
- pz_core/cli_options.py +74 -0
- pz_core/core/__init__.py +3 -0
- pz_core/core/errors/__init__.py +5 -0
- pz_core/core/errors/models.py +117 -0
- pz_core/core/events/__init__.py +5 -0
- pz_core/core/events/registry.py +54 -0
- pz_core/core/extensions/__init__.py +51 -0
- pz_core/core/extensions/registry.py +790 -0
- pz_core/core/health/__init__.py +6 -0
- pz_core/core/health/contracts.py +10 -0
- pz_core/core/middlewares/__init__.py +6 -0
- pz_core/core/middlewares/models.py +24 -0
- pz_core/core/routing/__init__.py +5 -0
- pz_core/core/routing/models.py +21 -0
- pz_core/core/settings_contracts.py +25 -0
- pz_core/docs/README.md +20 -0
- pz_core/docs/distribution-README.md +438 -0
- pz_core/docs/pz/345/221/275/344/273/244/346/200/273/350/247/210.md +1011 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/00-pz/346/236/266/346/236/204/346/200/273/347/272/262.md +240 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/350/247/204/350/214/203.md +216 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203.md +403 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/03-App/346/234/200/345/260/217/345/255/220/345/214/205/350/247/204/350/214/203.md +571 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/04-Settings/344/270/216/351/205/215/347/275/256/350/247/204/350/214/203.md +1177 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/05-/350/265/204/346/272/220/344/270/216/350/277/220/350/241/214/347/233/256/345/275/225/350/247/204/350/214/203.md +575 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/06-/346/225/260/346/215/256/345/272/223/344/270/216/345/217/257/351/200/211/345/237/272/347/241/200/350/256/276/346/226/275/350/247/204/350/214/203.md +499 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/07-App/346/233/277/346/215/242/350/247/204/350/214/203.md +206 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/08-/345/271/263/345/217/260/347/272/247App/344/270/216/350/277/220/350/241/214/346/227/266/345/215/225/344/276/213/350/247/204/350/214/203.md +445 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/09-CLI/344/270/216Worker/345/205/245/345/217/243/350/247/204/350/214/203.md +950 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/10-/346/265/213/350/257/225/344/270/216/345/245/221/347/272/246/350/247/204/350/214/203.md +1001 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/11-/344/273/223/345/272/223/347/234/237/347/233/270/346/272/220/345/234/260/345/233/276.md +102 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/12-/346/236/204/345/273/272/344/270/216/345/217/221/345/270/203/346/243/200/346/237/245/346/270/205/345/215/225.md +171 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/13-/346/255/243/345/274/217Facade/346/200/273/350/241/250.md +402 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/14-/345/210/206/345/261/202/344/270/216/346/211/251/345/261/225/350/247/204/350/214/203.md +550 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/15-/345/255/220/345/214/205/345/274/200/345/217/221/344/275/277/347/224/250/346/211/213/345/206/214.md +1004 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/16-/346/211/251/345/261/225/347/202/271/346/255/243/345/274/217/345/215/217/350/256/256/346/270/205/345/215/225.md +352 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/17-pz.protocols.v1/346/255/243/345/274/217/345/215/217/350/256/256.md +266 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/18-/346/211/251/345/261/225/347/202/271/345/206/262/347/252/201/350/243/201/345/206/263/346/200/273/350/241/250.md +131 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/19-/351/224/231/350/257/257/347/240/201/344/270/216/350/257/212/346/226/255/345/215/217/350/256/256.md +176 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/20-/347/247/201/346/234/211/345/214/205/344/273/223/344/270/216/347/273/237/344/270/200/350/256/244/350/257/201/351/203/250/347/275/262/350/247/204/350/214/203.md +243 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/21-contrib/347/272/246/345/256/232/344/270/216Provider/346/216/245/345/205/245/350/247/204/350/214/203.md +365 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/22-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203.md +912 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/23-/351/246/226/345/261/217/345/261/225/347/244/272/344/270/216/351/246/226/345/270/247/347/250/263/345/256/232/350/247/204/350/214/203.md +269 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/24-pz/345/220/257/345/212/250/344/270/216/350/277/220/350/241/214/346/227/266/347/274/226/350/257/221/346/265/201/347/250/213.md +245 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/25-/351/235/242/345/220/221/345/257/271/350/261/241/345/274/200/345/217/221/350/247/204/350/214/203.md +300 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/README.md +98 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/00-legacy_core/346/240/207/345/207/206pip/345/214/205/345/214/226/351/207/215/346/236/204/346/200/273/347/272/262.md +1073 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +224 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203-/345/220/253/345/216/206/345/217/262/345/217/243/345/276/204.md +671 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/13-/344/270/200/346/254/241/346/200/247/345/256/236/346/226/275/346/270/205/345/215/225.md +387 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/14-/345/255/220/345/214/205/351/200/202/351/205/215/346/224/271/351/200/240/346/270/205/345/215/225.md +592 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/15-infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/346/270/205/345/215/225.md +81 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/19-core/345/215/225/344/270/200/346/255/243/345/274/217/346/226/271/346/241/210/346/224/266/346/225/233/346/211/247/350/241/214/346/270/205/345/215/225.md +161 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/25-/345/215/217/350/256/256/345/210/260/346/272/220/347/240/201/345/267/256/350/267/235/346/270/205/345/215/225.md +377 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/26-cutover/345/256/214/345/267/245/346/243/200/346/237/245/346/270/205/345/215/225.md +117 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/29-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203-/345/220/253/350/277/201/347/247/273/347/255/226/347/225/245.md +956 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/99-/350/265/204/346/272/220/346/224/266/345/217/243/345/220/216/347/273/255TODO.md +84 -0
- pz_core/docs//345/217/221/347/211/210/345/211/215/346/270/205/345/215/225-4.0.11.md +84 -0
- pz_core/docs//346/226/260/346/211/213/346/214/207/345/215/227.md +422 -0
- pz_core/infra/__init__.py +17 -0
- pz_core/infra/db/__init__.py +23 -0
- pz_core/infra/db/contracts.py +73 -0
- pz_core/infra/health/__init__.py +5 -0
- pz_core/infra/health/contracts.py +8 -0
- pz_core/infra/kv/__init__.py +5 -0
- pz_core/infra/kv/contracts.py +17 -0
- pz_core/infra/observability/__init__.py +23 -0
- pz_core/infra/observability/contracts.py +142 -0
- pz_core/infra/resilience/__init__.py +5 -0
- pz_core/infra/resilience/contracts.py +17 -0
- pz_core/infra/resources/__init__.py +5 -0
- pz_core/infra/resources/contracts.py +16 -0
- pz_core/infra/settings/__init__.py +17 -0
- pz_core/infra/settings/models.py +81 -0
- pz_core/infra/storage/__init__.py +5 -0
- pz_core/infra/storage/ports.py +27 -0
- pz_core/project_root_commands.py +282 -0
- pz_core/project_runtime_cli.py +344 -0
- pz_core/project_sources.py +253 -0
- pz_core/pz_launcher.py +209 -0
- pz_core/root_cli.py +70 -0
- pz_core/runtime/__init__.py +229 -0
- pz_core/runtime/access.py +86 -0
- pz_core/runtime/adapters/__init__.py +5 -0
- pz_core/runtime/adapters/base.py +18 -0
- pz_core/runtime/asyncio_bridge.py +18 -0
- pz_core/runtime/bindings/__init__.py +5 -0
- pz_core/runtime/bindings/container.py +50 -0
- pz_core/runtime/cli/__init__.py +5 -0
- pz_core/runtime/cli/_args.py +67 -0
- pz_core/runtime/cli/app_commands.py +417 -0
- pz_core/runtime/cli/check.py +214 -0
- pz_core/runtime/cli/db.py +297 -0
- pz_core/runtime/cli/main.py +89 -0
- pz_core/runtime/cli/resources.py +280 -0
- pz_core/runtime/discovery.py +23 -0
- pz_core/runtime/environment_integrity.py +157 -0
- pz_core/runtime/errors.py +5 -0
- pz_core/runtime/events.py +87 -0
- pz_core/runtime/facades/__init__.py +38 -0
- pz_core/runtime/facades/_capabilities.py +46 -0
- pz_core/runtime/facades/cache.py +103 -0
- pz_core/runtime/facades/database.py +71 -0
- pz_core/runtime/facades/events.py +380 -0
- pz_core/runtime/facades/health.py +101 -0
- pz_core/runtime/facades/mail.py +121 -0
- pz_core/runtime/facades/observability.py +102 -0
- pz_core/runtime/facades/providers.py +75 -0
- pz_core/runtime/facades/resources.py +52 -0
- pz_core/runtime/facades/storage.py +296 -0
- pz_core/runtime/health/__init__.py +5 -0
- pz_core/runtime/health/service.py +102 -0
- pz_core/runtime/host.py +188 -0
- pz_core/runtime/host_runner.py +282 -0
- pz_core/runtime/http/__init__.py +20 -0
- pz_core/runtime/http/app.py +576 -0
- pz_core/runtime/http_timing.py +173 -0
- pz_core/runtime/invocation.py +29 -0
- pz_core/runtime/json_safe.py +47 -0
- pz_core/runtime/lifecycle/__init__.py +25 -0
- pz_core/runtime/lifecycle/manager.py +533 -0
- pz_core/runtime/plan.py +214 -0
- pz_core/runtime/plan_context.py +33 -0
- pz_core/runtime/plan_snapshot.py +177 -0
- pz_core/runtime/registries/__init__.py +27 -0
- pz_core/runtime/registries/capabilities.py +101 -0
- pz_core/runtime/registries/providers.py +396 -0
- pz_core/runtime/resource_assets.py +344 -0
- pz_core/runtime/run_cli.py +211 -0
- pz_core/runtime/settings.py +888 -0
- pz_core/runtime/worker/__init__.py +21 -0
- pz_core/runtime/worker/engine.py +921 -0
- pz_core/runtime/worker/runtime.py +25 -0
- pz_core/testing/__init__.py +15 -0
- pz_core/testing/doc_route_contracts.py +305 -0
- pz_core/utils/__init__.py +44 -0
- pz_core/utils/env.py +65 -0
- pz_core/utils/project_jsonc/__init__.py +29 -0
- pz_core/utils/project_jsonc/document.py +420 -0
- pz_core/utils/project_settings.py +99 -0
- pz_foundation/__init__.py +50 -0
- pz_foundation/_contracts.py +81 -0
- pz_foundation/_errors.py +34 -0
- pz_foundation/api.py +14 -0
- pz_foundation/apps.py +127 -0
- pz_foundation/auth/__init__.py +1 -0
- pz_foundation/cache_control.py +233 -0
- pz_foundation/cli/__init__.py +2 -0
- pz_foundation/cli/workers.py +565 -0
- pz_foundation/configs/__init__.py +0 -0
- pz_foundation/configs/default.env +15 -0
- pz_foundation/configs/default.jsonc +452 -0
- pz_foundation/db/__init__.py +94 -0
- pz_foundation/db/aerich_executor.py +664 -0
- pz_foundation/db/config.py +358 -0
- pz_foundation/db/connection_manager.py +689 -0
- pz_foundation/db/connection_probe.py +204 -0
- pz_foundation/db/database.py +1291 -0
- pz_foundation/db/deps.py +47 -0
- pz_foundation/db/lifecycle_status.py +85 -0
- pz_foundation/db/migrations.py +477 -0
- pz_foundation/db/mysql.py +57 -0
- pz_foundation/db/postgresql.py +409 -0
- pz_foundation/db/production_pool_fallback.py +143 -0
- pz_foundation/db/public.py +45 -0
- pz_foundation/db/session.py +369 -0
- pz_foundation/db/sqlite.py +15 -0
- pz_foundation/db/tortoise_manager.py +724 -0
- pz_foundation/db/tortoise_psycopg.py +148 -0
- pz_foundation/docs/README.md +18 -0
- pz_foundation/docs/distribution-README.md +319 -0
- pz_foundation/docs/infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/350/257/264/346/230/216.md +39 -0
- pz_foundation/docs//345/205/254/345/274/200/345/205/215/350/264/271/346/216/245/345/217/243/351/230/262/346/273/245/347/224/250/344/270/216/344/272/272/346/234/272/346/240/241/351/252/214.md +680 -0
- pz_foundation/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +288 -0
- pz_foundation/docs//350/267/257/347/224/261/344/270/216URL/345/244/204/347/220/206/350/247/204/350/214/203.md +119 -0
- pz_foundation/health.py +105 -0
- pz_foundation/host/__init__.py +11 -0
- pz_foundation/host/builder.py +34 -0
- pz_foundation/host/cache_runtime.py +44 -0
- pz_foundation/host/db/__init__.py +17 -0
- pz_foundation/host/db/aerich_config.py +36 -0
- pz_foundation/host/db/config.py +106 -0
- pz_foundation/host/db/connection_manager.py +48 -0
- pz_foundation/host/db/deps.py +5 -0
- pz_foundation/host/db/orm_retry_patch.py +566 -0
- pz_foundation/host/db/request_adapter.py +325 -0
- pz_foundation/host/db/router.py +28 -0
- pz_foundation/host/db/session.py +284 -0
- pz_foundation/host/db/tortoise_manager.py +10 -0
- pz_foundation/host/idempotency.py +132 -0
- pz_foundation/host/lifespan.py +247 -0
- pz_foundation/host/redis_runtime.py +100 -0
- pz_foundation/host/web.py +790 -0
- pz_foundation/infra_provider.py +236 -0
- pz_foundation/kv/__init__.py +25 -0
- pz_foundation/kv/cache.py +1166 -0
- pz_foundation/kv/deps.py +84 -0
- pz_foundation/kv/public.py +25 -0
- pz_foundation/kv/redis_manager.py +1531 -0
- pz_foundation/lazy_exports.py +43 -0
- pz_foundation/lazy_imports.py +124 -0
- pz_foundation/lifecycle.py +392 -0
- pz_foundation/managed_workers.py +1117 -0
- pz_foundation/middlewares/__init__.py +26 -0
- pz_foundation/middlewares/_settings.py +250 -0
- pz_foundation/middlewares/analytics.py +124 -0
- pz_foundation/middlewares/cache_control.py +156 -0
- pz_foundation/middlewares/compression.py +202 -0
- pz_foundation/middlewares/cors.py +62 -0
- pz_foundation/middlewares/geo_country.py +44 -0
- pz_foundation/middlewares/inner_router.py +50 -0
- pz_foundation/middlewares/rate_limited/__init__.py +3 -0
- pz_foundation/middlewares/rate_limited/key_limiter.py +115 -0
- pz_foundation/middlewares/request_logging.py +104 -0
- pz_foundation/middlewares/route_owner.py +309 -0
- pz_foundation/middlewares/security_csrf.py +44 -0
- pz_foundation/middlewares/security_headers/__init__.py +4 -0
- pz_foundation/middlewares/security_headers/conflicts.py +44 -0
- pz_foundation/middlewares/security_headers/csp_nonce.py +200 -0
- pz_foundation/middlewares/security_headers/security_headers.py +44 -0
- pz_foundation/migrations/__init__.py +1 -0
- pz_foundation/migrations/pz_foundation/__init__.py +1 -0
- pz_foundation/models.py +22 -0
- pz_foundation/notifications/__init__.py +23 -0
- pz_foundation/notifications/email.py +542 -0
- pz_foundation/notifications/public.py +31 -0
- pz_foundation/observability/__init__.py +71 -0
- pz_foundation/observability/http.py +34 -0
- pz_foundation/observability/logger/__init__.py +49 -0
- pz_foundation/observability/logger/_bot_settings.py +126 -0
- pz_foundation/observability/logger/bot_message.py +592 -0
- pz_foundation/observability/logger/core.py +385 -0
- pz_foundation/observability/logger/delivery_audit.py +84 -0
- pz_foundation/observability/logger/feishu_bot.py +199 -0
- pz_foundation/observability/logger/telegram_bot.py +194 -0
- pz_foundation/observability/metrics.py +365 -0
- pz_foundation/observability/runtime.py +68 -0
- pz_foundation/public.py +405 -0
- pz_foundation/resources/__init__.py +12 -0
- pz_foundation/resources/builder.py +900 -0
- pz_foundation/resources/images.py +363 -0
- pz_foundation/root_cli.py +20 -0
- pz_foundation/routing/__init__.py +203 -0
- pz_foundation/routing/canonical.py +486 -0
- pz_foundation/routing/convertors.py +82 -0
- pz_foundation/routing/declarations.py +843 -0
- pz_foundation/routing/exports.py +148 -0
- pz_foundation/routing/loaders.py +138 -0
- pz_foundation/routing/manifest.py +215 -0
- pz_foundation/routing/manifest_provider.py +47 -0
- pz_foundation/routing/mounts.py +101 -0
- pz_foundation/routing/ordering.py +172 -0
- pz_foundation/routing/registry.py +133 -0
- pz_foundation/routing/utils.py +28 -0
- pz_foundation/runtime.py +234 -0
- pz_foundation/settings.py +253 -0
- pz_foundation/settings_normalization.py +130 -0
- pz_foundation/storage/__init__.py +25 -0
- pz_foundation/storage/local.py +209 -0
- pz_foundation/storage/public.py +106 -0
- pz_foundation/storage/s3.py +116 -0
- pz_foundation/storage/service.py +234 -0
- pz_foundation/urls.py +19 -0
- pz_foundation/utils/__init__.py +6 -0
- pz_foundation/utils/merge.py +160 -0
- pz_foundation/worker_replicas.py +55 -0
- pz_settings_center/__init__.py +5 -0
- pz_settings_center/api/__init__.py +5 -0
- pz_settings_center/api/runtime_overrides.py +360 -0
- pz_settings_center/apps.py +28 -0
- pz_settings_center/configs/__init__.py +0 -0
- pz_settings_center/configs/default.env +2 -0
- pz_settings_center/configs/default.jsonc +1 -0
- pz_settings_center/docs/01-settings_center/350/203/275/345/212/233/350/276/271/347/225/214.md +124 -0
- pz_settings_center/docs/README.md +12 -0
- pz_settings_center/docs/distribution-README.md +150 -0
- pz_settings_center/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +48 -0
- pz_settings_center/integration_payload.py +18 -0
- pz_settings_center/project_helpers.py +405 -0
- pz_settings_center/public.py +139 -0
- pz_settings_center/routing_helpers.py +73 -0
- pz_settings_center/runtime_override_provider.py +61 -0
- pz_settings_center/typed_settings.py +324 -0
- pz_settings_center/urls.py +9 -0
|
@@ -0,0 +1,956 @@
|
|
|
1
|
+
# 29-多国语言资源治理与按需加载规范
|
|
2
|
+
|
|
3
|
+
> 本文档用于定义 `pz` 体系下多国语言资源的正式治理边界与按需加载主链。本文档服从 `00-pz架构总纲`、`05-资源与运行目录规范`、`17-分层与扩展规范`、`21-扩展点正式协议清单` 的上位约束。
|
|
4
|
+
|
|
5
|
+
## 1. 目标
|
|
6
|
+
|
|
7
|
+
本文档解决三个长期问题:
|
|
8
|
+
|
|
9
|
+
- 避免 `base` 持续膨胀,重新变成“全站公共垃圾桶”
|
|
10
|
+
- 避免页面把整站翻译全集注入 HTML 或一次性下发给前端
|
|
11
|
+
- 在不引入第二套资源协议的前提下,实现清晰的翻译 owner、稳定的 fallback 规则与按需取用能力
|
|
12
|
+
|
|
13
|
+
本文档的正式目标不是“再造一套 i18n 框架”,而是在现有 `resource manifest` 主链上,补齐多国语言资源的正式治理约束。
|
|
14
|
+
|
|
15
|
+
## 2. 设计哲学对齐
|
|
16
|
+
|
|
17
|
+
本方案必须同时满足 `pz` 的以下设计哲学:
|
|
18
|
+
|
|
19
|
+
- 最小子包是唯一正式单位,不是 `base`、不是宿主目录分组、也不是页面临时约定
|
|
20
|
+
- `pz` 只保留稳定合同、发现、编译、组装与启动,不承载高层业务翻译实现
|
|
21
|
+
- 一个问题只保留一种正式解决方案,不允许出现第二套翻译装配主链
|
|
22
|
+
- 资源进入系统的唯一正式入口仍然是 `resource.contribution -> resource manifest`
|
|
23
|
+
- Web / CLI / worker 只消费 `RuntimePlan` 与其中的 `resource_manifest`
|
|
24
|
+
- 共享能力进入共享子包,业务能力进入业务子包,宿主只做装配
|
|
25
|
+
|
|
26
|
+
因此:
|
|
27
|
+
|
|
28
|
+
- 不允许再新增一套独立的 `translations registry`、`page resources DSL`、`surface bootstrap` 之类第二主链
|
|
29
|
+
- 不允许页面自己直接扫描 `locales/`、拼接路径、维护局部 fallback 规则
|
|
30
|
+
- 不允许通过“先塞进 `base`,之后再看是否迁移”作为长期治理方案
|
|
31
|
+
|
|
32
|
+
## 3. 非目标
|
|
33
|
+
|
|
34
|
+
本文档不做以下事情:
|
|
35
|
+
|
|
36
|
+
- 不定义新的模板引擎或新的前端国际化框架
|
|
37
|
+
- 不要求所有历史 key 立刻一次性迁移完成
|
|
38
|
+
- 不把具体页面文案 owner 直接硬编码进 `pz` core
|
|
39
|
+
- 不允许为了 i18n 治理,在 `pz` 基础层引入高层页面/业务实现
|
|
40
|
+
|
|
41
|
+
## 4. 正式术语
|
|
42
|
+
|
|
43
|
+
### 4.1 `locale owner`
|
|
44
|
+
|
|
45
|
+
某个翻译资源的正式归属最小子包。
|
|
46
|
+
|
|
47
|
+
规则:
|
|
48
|
+
|
|
49
|
+
- 每个翻译 key 必须先有唯一 `locale owner`
|
|
50
|
+
- `locale owner` 只能是最小子包
|
|
51
|
+
- `base` 不是 owner,只是少量共享文案面的物理承载位置
|
|
52
|
+
|
|
53
|
+
### 4.2 `locale bundle`
|
|
54
|
+
|
|
55
|
+
运行时的最小翻译消费单位。
|
|
56
|
+
|
|
57
|
+
一个 `locale bundle` 表示:
|
|
58
|
+
|
|
59
|
+
- 某个 owner
|
|
60
|
+
- 某个稳定文案面
|
|
61
|
+
- 某个语言版本
|
|
62
|
+
|
|
63
|
+
`locale bundle` 是运行时和前端正式消费的最小单位,不再以“整个语言全集”作为默认消费单位。
|
|
64
|
+
|
|
65
|
+
### 4.3 `shared bundle`
|
|
66
|
+
|
|
67
|
+
由共享子包持有、可被其他 bundle 显式依赖的共享翻译面。
|
|
68
|
+
|
|
69
|
+
它的职责是承载跨页面、跨模块、跨业务仍然稳定复用的共享文案块。
|
|
70
|
+
|
|
71
|
+
典型内容:
|
|
72
|
+
|
|
73
|
+
- 导航
|
|
74
|
+
- footer
|
|
75
|
+
- 通用按钮
|
|
76
|
+
- 共享首帧 UI
|
|
77
|
+
- 共享壳层 SEO 文案
|
|
78
|
+
|
|
79
|
+
### 4.4 `page-type bundle`
|
|
80
|
+
|
|
81
|
+
由某个 owner app 持有、供一类页面重复复用的翻译面。
|
|
82
|
+
|
|
83
|
+
它的职责是承载“同类型页面”的通用文案结构,而不是单页面独有文案。
|
|
84
|
+
|
|
85
|
+
典型场景:
|
|
86
|
+
|
|
87
|
+
- SEO keyword landing 页
|
|
88
|
+
- tool landing 页
|
|
89
|
+
- pricing landing 页
|
|
90
|
+
- FAQ landing 页
|
|
91
|
+
|
|
92
|
+
### 4.5 `bundle dependency`
|
|
93
|
+
|
|
94
|
+
某个 bundle 允许依赖哪些共享 bundle 的正式声明。
|
|
95
|
+
|
|
96
|
+
它用于限定 fallback 和按需加载边界,不允许 bundle 之间任意互相吃文案。
|
|
97
|
+
|
|
98
|
+
### 4.6 `locale governance metadata`
|
|
99
|
+
|
|
100
|
+
用于描述 `locale bundle`、`bundle dependency`、共享 allowlist 与消费绑定关系的标准资源元数据。
|
|
101
|
+
|
|
102
|
+
正式要求:
|
|
103
|
+
|
|
104
|
+
- 它必须通过 `apps.py -> extensions() -> resource.contribution` 声明
|
|
105
|
+
- 它必须通过统一 resource compiler 进入 `resource_manifest`
|
|
106
|
+
- 它不是新的扩展点协议,也不是页面侧独立 DSL
|
|
107
|
+
|
|
108
|
+
历史说明:
|
|
109
|
+
|
|
110
|
+
- `locales/config.json` 属于历史遗留设计
|
|
111
|
+
- 它将“翻译内容”和“翻译治理配置”混放在 `locales/` 下
|
|
112
|
+
- 本轮正式方案不保留兼容,不再作为正式声明点
|
|
113
|
+
|
|
114
|
+
### 4.7 `locale consumer binding`
|
|
115
|
+
|
|
116
|
+
某个正式消费单元与其允许消费的 bundle 集合之间的绑定关系。
|
|
117
|
+
|
|
118
|
+
这里的“消费单元”不是宿主临时对象,而是 owner app 控制下的正式渲染单元,例如:
|
|
119
|
+
|
|
120
|
+
- 路由渲染单元
|
|
121
|
+
- 模板入口单元
|
|
122
|
+
- 前端模块入口单元
|
|
123
|
+
|
|
124
|
+
它的声明必须来自 owner app 的 `resource.contribution` 元数据,并统一编译进 `resource manifest`。
|
|
125
|
+
|
|
126
|
+
### 4.8 `locale composition profile`
|
|
127
|
+
|
|
128
|
+
用于描述一类页面或渲染单元应如何组合多个 bundle 的结构化配置。
|
|
129
|
+
|
|
130
|
+
它的设计目标是支持这类正式场景:
|
|
131
|
+
|
|
132
|
+
- SEO 页面一个页面对应一个关键词
|
|
133
|
+
- 新增关键词页面时,不必复制一整套翻译装配规则
|
|
134
|
+
- owner app 只需新增或调整 profile 配置,即可复用既有共享 bundle 与页面 bundle 组合
|
|
135
|
+
|
|
136
|
+
正式要求:
|
|
137
|
+
|
|
138
|
+
- profile 必须由 owner app 在 `resource.contribution` 元数据中声明
|
|
139
|
+
- profile 只能组合已声明的 bundle 与 dependency
|
|
140
|
+
- profile 不得在运行时临时拼接第二套规则
|
|
141
|
+
- 页面级语言事实必须收敛到 `page_supported_language`,用于约束当前页面实际支持、可注册、可输出 canonical / hreflang / sitemap 的语言范围
|
|
142
|
+
- 页面语言切换 URL 必须收敛到 `page_language_alternates`,用于表达当前页面各语言版本的 canonical alternate URL;全站语言全集只称 `routing.all_supported_languages`
|
|
143
|
+
|
|
144
|
+
### 4.8.1 `page_supported_language`
|
|
145
|
+
|
|
146
|
+
某个 `page_config` / `page_profile` 正式支持的页面级语言集合。
|
|
147
|
+
|
|
148
|
+
它用于统一约束以下能力:
|
|
149
|
+
|
|
150
|
+
- `seo.provider` 暴露的 hreflang 语言范围
|
|
151
|
+
- `seo.sitemap_provider` 暴露的 sitemap 语言范围
|
|
152
|
+
- 页面是否对某语言实际开放
|
|
153
|
+
- 页面语言切换候选语言范围
|
|
154
|
+
|
|
155
|
+
正式要求:
|
|
156
|
+
|
|
157
|
+
- `page_supported_language` 必须作为页面级结构化字段进入页面编译结果和请求期页面状态
|
|
158
|
+
- `page_supported_language` 必须是顶层 `routing.all_supported_languages` 的子集;顶层语言全集只负责 URL 语言前缀归一化、alias 解析和系统级合法性判断
|
|
159
|
+
- 若某页面只允许一个语言,则 `page_supported_language` 只能包含该语言
|
|
160
|
+
- 不在 `page_supported_language` 内的语言,不得出现在页面注册、`hreflang` 或 sitemap 中
|
|
161
|
+
- fallback 仅影响内容兜底,不得自动扩张 `page_supported_language`
|
|
162
|
+
- 对 owner app 的正式公开面应尽量收敛到单个 `page_profile` / `page_config` 解析入口;`page_supported_language` 作为页面合同字段暴露,而不是再额外公开第二个“裸语言列表”入口
|
|
163
|
+
- `seo.provider`、`seo.sitemap_provider` 等共享消费方应统一从页面编译结果或请求期页面状态读取 `page_supported_language`,不要要求业务子包分别实现多套语言解析 surface
|
|
164
|
+
- 若页面级 profile 需要跨子包被共享消费,正式接入方式应是 `PageProfileProviderSpec(kind="page.profile_provider")` 扩展点,而不是业务子包在 `public.py` 继续暴露自定义 resolver
|
|
165
|
+
|
|
166
|
+
### 4.8.2 `page_language_alternates`
|
|
167
|
+
|
|
168
|
+
当前页面各语言版本 canonical alternate URL 的页面级投影。
|
|
169
|
+
|
|
170
|
+
它用于表达“当前内容实体在某语言下的正式跳转 URL”,不由前端根据全站语言全集临时拼接。典型场景是页面 owner 在渲染前把当前页面的多语言 canonical 写入 `request.state.page_language_alternates`,共享 UI、footer 语言链接和前端 `window.changeLanguage` 消费同一个 SSR 投影。
|
|
171
|
+
|
|
172
|
+
正式要求:
|
|
173
|
+
|
|
174
|
+
- `page_language_alternates` 必须作为请求期页面状态或 SEO hreflang 同合同投影进入模板运行态;不得另建独立 resolver / getter 作为第二真相源
|
|
175
|
+
- `page_language_alternates` 的语言成员必须来自当前页面的 `page_supported_language`
|
|
176
|
+
- 若当前页面缺少 `page_language_alternates`,共享 UI 只允许按正式合同使用 SEO hreflang 投影或当前页面事实做兜底;不得用 `routing.all_supported_languages` 自动扩展当前页面可切换语言
|
|
177
|
+
- 若请求期页面状态显式声明 `page_language_alternates = []`,表示该页面不提供语言切换 URL;共享 UI 层不得继续回退到模块或全站语言列表
|
|
178
|
+
- `page_language_alternates` 只表达语言切换 URL,不得扩张 `hreflang`、sitemap、canonical 语言族或索引资格
|
|
179
|
+
|
|
180
|
+
### 4.9 `page-local override`
|
|
181
|
+
|
|
182
|
+
某个具体页面对通用 bundle 的稀疏覆盖补丁。
|
|
183
|
+
|
|
184
|
+
它的职责不是保存整页全文案,而是处理极少数单页面特化字段。
|
|
185
|
+
|
|
186
|
+
正式要求:
|
|
187
|
+
|
|
188
|
+
- override 默认不是常规路径,而是受限例外能力
|
|
189
|
+
- override 只能服务单页面特化,不得复制整页 bundle
|
|
190
|
+
- override 必须与具体 profile 显式绑定
|
|
191
|
+
- override 必须通过 `resource.contribution` 元数据声明,并统一编译进 `resource manifest`
|
|
192
|
+
|
|
193
|
+
治理约束:
|
|
194
|
+
|
|
195
|
+
- override 必须是稀疏 patch,而不是整页 JSON 副本
|
|
196
|
+
- override 只允许覆盖白名单字段
|
|
197
|
+
- 若 override 体积持续增大,应优先重构 page-type bundle,而不是继续 patch
|
|
198
|
+
|
|
199
|
+
## 5. 正式治理边界
|
|
200
|
+
|
|
201
|
+
### 5.1 owner 先于目录
|
|
202
|
+
|
|
203
|
+
翻译归属必须先判 owner,再判物理目录。
|
|
204
|
+
|
|
205
|
+
禁止:
|
|
206
|
+
|
|
207
|
+
- 先因为“方便复用”把 key 放进 `base`
|
|
208
|
+
- 先因为“页面暂时缺词”把业务 key 放进共享目录
|
|
209
|
+
- 通过目录名掩盖真实 owner
|
|
210
|
+
|
|
211
|
+
### 5.2 共享文案归共享子包
|
|
212
|
+
|
|
213
|
+
共享站点壳、共享首帧、共享导航、共享按钮、认证、协议页等共享文案,必须归对应共享子包。
|
|
214
|
+
|
|
215
|
+
典型 owner:
|
|
216
|
+
|
|
217
|
+
- shared site UI owner
|
|
218
|
+
- auth owner
|
|
219
|
+
- site essentials / policy owner
|
|
220
|
+
|
|
221
|
+
### 5.3 业务文案归业务子包
|
|
222
|
+
|
|
223
|
+
首页、pricing、FAQ、tools、业务 SEO、业务 JSON-LD、业务转化文案,必须归业务子包本身。
|
|
224
|
+
|
|
225
|
+
宿主或业务 app 不得把这些内容长期塞进共享 `base`。
|
|
226
|
+
|
|
227
|
+
### 5.4 `base` 的正式地位
|
|
228
|
+
|
|
229
|
+
`base` 不是第二业务目录,只是“共享稳定文案面”的受限承载位置。
|
|
230
|
+
|
|
231
|
+
`base` 只能保留 allowlist 范围内的共享 bundle。
|
|
232
|
+
|
|
233
|
+
## 6. 正式资源模型
|
|
234
|
+
|
|
235
|
+
### 6.1 保持单一资源主链
|
|
236
|
+
|
|
237
|
+
多国语言资源仍然属于正式 `resource.contribution` 的一部分。
|
|
238
|
+
|
|
239
|
+
每个 app 继续只提交自己的:
|
|
240
|
+
|
|
241
|
+
- `templates_dir`
|
|
242
|
+
- `static_dir`
|
|
243
|
+
- `locales_dir`
|
|
244
|
+
|
|
245
|
+
统一进入 `resource manifest`。
|
|
246
|
+
|
|
247
|
+
不允许:
|
|
248
|
+
|
|
249
|
+
- 额外新增 `translations()` / `surfaces()` / `page_resources()` 之类第二协议
|
|
250
|
+
- 在 Web 启动或请求路径中再次重扫 `locales/`
|
|
251
|
+
|
|
252
|
+
### 6.2 在 `resource manifest` 上补足翻译元信息
|
|
253
|
+
|
|
254
|
+
对于 `locales_dir`,正式编译链应补充但不限于以下元信息:
|
|
255
|
+
|
|
256
|
+
- `owner_app`
|
|
257
|
+
- `bundle_id`
|
|
258
|
+
- `language`
|
|
259
|
+
- `source_path`
|
|
260
|
+
- `fallback_policy`
|
|
261
|
+
- `bundle_dependencies`
|
|
262
|
+
|
|
263
|
+
这些元信息属于 `resource manifest` 的翻译资源视图,不构成第二套装配协议。
|
|
264
|
+
|
|
265
|
+
声明约束:
|
|
266
|
+
|
|
267
|
+
- 上述元信息只允许来自:
|
|
268
|
+
- 标准目录布局
|
|
269
|
+
- `resource.contribution` 的结构化元数据
|
|
270
|
+
- 不允许页面、模板或前端脚本在运行时自行声明 bundle / dependency / fallback
|
|
271
|
+
|
|
272
|
+
推荐字段清单:
|
|
273
|
+
|
|
274
|
+
- `locale_bundles`
|
|
275
|
+
- 描述当前 app 提供的 `shared bundle` 与 `page-type bundle`
|
|
276
|
+
- `locale_dependencies`
|
|
277
|
+
- 描述 bundle 之间的显式依赖关系
|
|
278
|
+
- `locale_consumer_bindings`
|
|
279
|
+
- 描述正式消费单元可消费哪些 bundle
|
|
280
|
+
- `locale_profiles`
|
|
281
|
+
- 描述页面组合定义、SEO keyword page 组合规则与页面语言暴露集合
|
|
282
|
+
- `locale_overrides`
|
|
283
|
+
- 描述受限 `page-local override`
|
|
284
|
+
- `locale_allowlist`
|
|
285
|
+
- 描述当前 app 的 `base` 共享面 allowlist
|
|
286
|
+
|
|
287
|
+
这些字段属于 `resource.contribution` 的正式结构化元数据建议清单。后续协议落地时,应收敛为唯一字段集合,不允许各 app 自行扩展第二套命名。
|
|
288
|
+
|
|
289
|
+
### 6.3 真相源唯一
|
|
290
|
+
|
|
291
|
+
翻译运行时的唯一真相源仍然是当前 `RuntimePlan.resource_manifest` 及其对应的合并产物。
|
|
292
|
+
|
|
293
|
+
不允许:
|
|
294
|
+
|
|
295
|
+
- import-time 绑定 merged locales
|
|
296
|
+
- 模块级全局 translation manager 长期持有旧快照
|
|
297
|
+
- 页面脚本绕过 manifest 直接读磁盘资源
|
|
298
|
+
|
|
299
|
+
### 6.4 推荐源码布局
|
|
300
|
+
|
|
301
|
+
本方案推荐把“翻译内容”和“页面组合定义”物理拆开。
|
|
302
|
+
|
|
303
|
+
推荐目录:
|
|
304
|
+
|
|
305
|
+
- `locales/`
|
|
306
|
+
- 只放翻译内容
|
|
307
|
+
- 推荐按 `owner / lang / bundles` 或同等清晰层次组织
|
|
308
|
+
- `page_profiles/`
|
|
309
|
+
- 只放页面组合定义
|
|
310
|
+
- 推荐用于 SEO keyword page、tool landing page、FAQ landing page 这类组合型页面
|
|
311
|
+
- `locale_overrides/` 或等价目录
|
|
312
|
+
- 只放受限的 page-local override
|
|
313
|
+
- 仅在确有必要时启用
|
|
314
|
+
|
|
315
|
+
推荐示意:
|
|
316
|
+
|
|
317
|
+
- `page_profiles/example-topic-landing.jsonc`
|
|
318
|
+
- `locales/example_app/en/bundles/topic_landing.json`
|
|
319
|
+
- `locales/example_app/zh-CN/bundles/topic_landing.json`
|
|
320
|
+
- `locale_overrides/example-topic-landing/en.json`
|
|
321
|
+
|
|
322
|
+
约束:
|
|
323
|
+
|
|
324
|
+
- 目录结构只是源码组织建议,正式协议仍只认 `resource.contribution` 与编译结果
|
|
325
|
+
- `page_profiles/` 不得重新变成第二套运行时装配入口
|
|
326
|
+
- `locales/` 不再承担页面组合配置职责
|
|
327
|
+
|
|
328
|
+
## 7. 正式消费模型
|
|
329
|
+
|
|
330
|
+
### 7.1 bundle 是唯一正式消费单位
|
|
331
|
+
|
|
332
|
+
运行时和前端只允许按 `locale bundle` 消费翻译资源。
|
|
333
|
+
|
|
334
|
+
不允许:
|
|
335
|
+
|
|
336
|
+
- 读取整个语言全集作为默认正式路径
|
|
337
|
+
- 页面按目录扫描或文件名拼接来构造文案集
|
|
338
|
+
|
|
339
|
+
### 7.2 页面必须按声明取用
|
|
340
|
+
|
|
341
|
+
正式消费单元只能消费:
|
|
342
|
+
|
|
343
|
+
- 自己所属 owner 的 bundle
|
|
344
|
+
- 自己显式声明依赖的共享 bundle
|
|
345
|
+
- 与当前 profile 显式绑定的 page-local override
|
|
346
|
+
|
|
347
|
+
未声明的 bundle 不得被默认消费。
|
|
348
|
+
|
|
349
|
+
这里的“显式声明”不是宿主局部约定,而应由 owner app 在 `resource.contribution` 元数据中声明,并由 resource compiler 编译进 `resource manifest`。
|
|
350
|
+
|
|
351
|
+
### 7.3 SSR 与 CSR 统一主链
|
|
352
|
+
|
|
353
|
+
正式要求:
|
|
354
|
+
|
|
355
|
+
- SSR 只注入首帧所必需的最小 bundle
|
|
356
|
+
- CSR 通过统一 translation facade 按 bundle 懒加载剩余翻译
|
|
357
|
+
- 不再提供“页面加载时把所有翻译全集一次性注入 HTML”的正式路径
|
|
358
|
+
- SEO 关键页面与其他外部可见公开页面的首屏可见文案必须以后端渲染为正式主链,不得依赖前端二次请求翻译后再补齐正文
|
|
359
|
+
- 上述页面的前端翻译请求正式职责仅限于交互增强、异步组件和缺词补拉,不得反向成为首屏文案真相源
|
|
360
|
+
- `admin`、内部运营页、自用后台、强登录态工作台不强制纳入这条 SEO/public 首屏翻译约束
|
|
361
|
+
|
|
362
|
+
### 7.3.1 首屏后端直出约束
|
|
363
|
+
|
|
364
|
+
这里的“首屏可见文案”至少包括:
|
|
365
|
+
|
|
366
|
+
- 页面标题区
|
|
367
|
+
- 首屏 CTA
|
|
368
|
+
- 首屏正文摘要
|
|
369
|
+
- FAQ 首屏可见问答
|
|
370
|
+
- 首屏可见 SEO 文案块
|
|
371
|
+
- 与当前页面绑定的结构化数据、meta 描述、hreflang 所依赖的稳定语言内容
|
|
372
|
+
|
|
373
|
+
正式要求:
|
|
374
|
+
|
|
375
|
+
- owner app 必须先按 `page_profile`、bundle 组合、当前语言与 fallback 规则,在服务端完成首屏翻译 payload 裁剪
|
|
376
|
+
- 模板上下文或页面注入数据只允许携带“当前页面首屏实际需要的最小翻译集合”
|
|
377
|
+
- 前端 `translation facade` 必须优先消费服务端已注入的页面级翻译 payload
|
|
378
|
+
- 只有当异步组件、延后渲染区块或用户触发交互确实需要额外词条时,才允许再次请求翻译接口
|
|
379
|
+
|
|
380
|
+
### 7.3.1.1 请求期运行时合同
|
|
381
|
+
|
|
382
|
+
对外可见且需要 SEO 的 HTML 页面,在进入共享模板或共享翻译 facade 之前,必须先把页面翻译上下文绑定到统一的请求期合同。
|
|
383
|
+
|
|
384
|
+
正式要求:
|
|
385
|
+
|
|
386
|
+
- 该绑定必须由 owner app 或 site runtime 在进入模板前一次性完成
|
|
387
|
+
- 共享层应优先消费请求期合同,不得要求每条路由各自重新解析一遍 `page_profile`
|
|
388
|
+
- `pz` 基础层不承载这类页面装配细节;正式 helper 应放在 `pz_contrib` 或业务扩展层
|
|
389
|
+
|
|
390
|
+
公共 HTML 页请求期合同至少应包含:
|
|
391
|
+
|
|
392
|
+
- `request.state.lang`
|
|
393
|
+
- `request.state.translation_module`
|
|
394
|
+
- `request.state.page_profile`
|
|
395
|
+
- `request.state.translation_bundles`
|
|
396
|
+
- `request.state.client_translation_scopes`
|
|
397
|
+
- `request.state.page_supported_language`
|
|
398
|
+
- `request.state.page_language_alternates`
|
|
399
|
+
|
|
400
|
+
约束:
|
|
401
|
+
|
|
402
|
+
- `translation_bundles` 是共享翻译 facade 的正式 bundle 组合真相源
|
|
403
|
+
- `client_translation_scopes` 是前端增量取词范围的正式真相源
|
|
404
|
+
- `page_supported_language` 是页面注册、`hreflang`、`sitemap` 与公开索引语言范围的正式页面级真相源
|
|
405
|
+
- `page_language_alternates` 是页面语言切换 URL 的正式 SSR 真相源;缺省时共享层只能消费 SEO hreflang 同合同投影或当前页面事实兜底,不得自动扩展到全站语言全集
|
|
406
|
+
- 对业务子包来说,正式页面公开面应优先收敛为单个 `page_profile` / `page_config`;`request.state.page_supported_language` 只是共享层从页面合同派生出来的消费态字段,不应倒逼每个子包长期维护第二套独立公开接口
|
|
407
|
+
- 纯 SSR 页面也应产出稳定字段形态;若无需前端增量取词,`client_translation_scopes` 应显式为空集合,而不是依赖运行时隐式缺省
|
|
408
|
+
- 多个页面 runtime 不得在各自路由里长期手写同构的 `request.state` 注入逻辑,应通过共享 helper 统一绑定
|
|
409
|
+
|
|
410
|
+
回退规则:
|
|
411
|
+
|
|
412
|
+
- 共享层可在兼容期内从 `page_profile` 推导缺失字段
|
|
413
|
+
- 但该回退只可作为容错,不得继续演化成第二套正式主链
|
|
414
|
+
|
|
415
|
+
禁止:
|
|
416
|
+
|
|
417
|
+
- 首屏正文依赖前端请求 `/translations.json` 后再补齐
|
|
418
|
+
- 为了省事把整个模块甚至整站翻译全集内联进 HTML
|
|
419
|
+
- 让 CSR 在首屏阶段重新计算一套独立于 SSR 的 bundle 组合
|
|
420
|
+
- 把页面级翻译请求数量收敛问题,退化为“继续让前端多次串行取词”
|
|
421
|
+
|
|
422
|
+
推荐结果:
|
|
423
|
+
|
|
424
|
+
- 对可缓存的 SEO/landing/公开关键词页/博客/帮助页等公开页面,首屏翻译网络请求目标应为 `0`
|
|
425
|
+
- 若存在首屏后额外翻译请求,也只应来自非首屏交互区块或缺词兜底
|
|
426
|
+
|
|
427
|
+
推荐的稳定装配顺序固定为:
|
|
428
|
+
|
|
429
|
+
1. `shared bundle`
|
|
430
|
+
2. `page-type bundle`
|
|
431
|
+
3. `page-local override`
|
|
432
|
+
|
|
433
|
+
正式限制:
|
|
434
|
+
|
|
435
|
+
- 覆盖层级最多只允许上述三层
|
|
436
|
+
- 不允许 host、页面脚本、模板 helper 在这三层之外再追加第四层覆盖
|
|
437
|
+
|
|
438
|
+
### 7.3.2 正式优先级表
|
|
439
|
+
|
|
440
|
+
正式优先级固定如下:
|
|
441
|
+
|
|
442
|
+
1. `shared bundle`
|
|
443
|
+
2. `page-type bundle`
|
|
444
|
+
3. `page-local override`
|
|
445
|
+
|
|
446
|
+
语义说明:
|
|
447
|
+
|
|
448
|
+
- `shared bundle`
|
|
449
|
+
- 提供共享稳定壳层文案
|
|
450
|
+
- 不感知单页面特化
|
|
451
|
+
- `page-type bundle`
|
|
452
|
+
- 提供同类页面通用文案结构
|
|
453
|
+
- 允许覆盖共享层中被设计为页面可定制的字段
|
|
454
|
+
- `page-local override`
|
|
455
|
+
- 只允许覆盖白名单字段
|
|
456
|
+
- 只服务单页面极少量特化
|
|
457
|
+
|
|
458
|
+
正式要求:
|
|
459
|
+
|
|
460
|
+
- 同一 key 的正式覆盖链最多经过一次设计性覆盖
|
|
461
|
+
- 如果某 key 同时在多个同层 bundle 中被声明,编译期必须直接报冲突
|
|
462
|
+
- 如果某 key 不在 override 白名单中,`page-local override` 不得覆盖它
|
|
463
|
+
- 不允许在运行时再引入“最后加载者生效”一类隐式优先级
|
|
464
|
+
|
|
465
|
+
### 7.4 预编译与请求期约束
|
|
466
|
+
|
|
467
|
+
生产正式要求:
|
|
468
|
+
|
|
469
|
+
- bundle 解析、profile 解析、dependency 裁决必须在资源编译阶段完成
|
|
470
|
+
- 运行时只允许消费已经进入 `resource manifest` 的编译结果
|
|
471
|
+
- 请求期不允许再次重扫 `locales/`、重建 profile、重做深层 merge
|
|
472
|
+
- 请求期允许的动态行为只应包括:
|
|
473
|
+
- 按当前语言选择已编译 bundle
|
|
474
|
+
- 按当前 profile 选择已编译组合结果
|
|
475
|
+
- 对受控变量做轻量字符串插值
|
|
476
|
+
|
|
477
|
+
禁止:
|
|
478
|
+
|
|
479
|
+
- 在请求期根据页面 slug 临时推导第二套 bundle 组合规则
|
|
480
|
+
- 在请求期将多个大 JSON 反复深度 merge 后再输出
|
|
481
|
+
- 将“开发期方便”路径扩散为生产正式路径
|
|
482
|
+
|
|
483
|
+
### 7.5 缓存与失效策略
|
|
484
|
+
|
|
485
|
+
生产正式要求:
|
|
486
|
+
|
|
487
|
+
- 缓存粒度必须围绕正式消费单位设计,至少可稳定定位到:
|
|
488
|
+
- `owner_app`
|
|
489
|
+
- `language`
|
|
490
|
+
- `bundle_id` 或 `profile_id`
|
|
491
|
+
- 缓存失效只能由正式资源版本或正式部署切换触发
|
|
492
|
+
- dev / test / product 的差异只能体现在缓存策略上,不能体现在消费语义上
|
|
493
|
+
|
|
494
|
+
推荐原则:
|
|
495
|
+
|
|
496
|
+
- bundle 级缓存优先于页面全集缓存
|
|
497
|
+
- profile 级缓存只缓存编译结果,不缓存运行时隐式推导
|
|
498
|
+
- 若 profile 组合结果可预编译,应优先预编译,而不是在请求期临时拼接
|
|
499
|
+
|
|
500
|
+
### 7.6 SEO 关键词页扩张规则
|
|
501
|
+
|
|
502
|
+
本方案必须天然支持 SEO 关键词页扩张,而不引入文案复制和结构失控。
|
|
503
|
+
|
|
504
|
+
正式要求:
|
|
505
|
+
|
|
506
|
+
- 新增关键词页时,默认流程应是:
|
|
507
|
+
1. 新增或调整一个 `composition profile`
|
|
508
|
+
2. 复用既有 `shared bundle`
|
|
509
|
+
3. 复用既有 `page-type bundle`
|
|
510
|
+
4. 只有在确有必要时才新增受限 `page-local override`
|
|
511
|
+
- 不允许把“新增关键词页”默认等价为“复制一整页翻译 JSON”
|
|
512
|
+
- 若多个关键词页出现大面积重复 override,应优先抽回 `page-type bundle`
|
|
513
|
+
|
|
514
|
+
生产目标:
|
|
515
|
+
|
|
516
|
+
- 新增 SEO 页面主要靠 profile 组合,而不是复制文案
|
|
517
|
+
- 关键词扩张不应导致翻译体系线性膨胀
|
|
518
|
+
- 关键词业务增长时,翻译体积增长应明显小于页面数量增长
|
|
519
|
+
|
|
520
|
+
### 7.6.1 profile 最小职责边界
|
|
521
|
+
|
|
522
|
+
`page_profile` 的正式职责只允许包括:
|
|
523
|
+
|
|
524
|
+
- 页面身份
|
|
525
|
+
- route 绑定
|
|
526
|
+
- template 绑定
|
|
527
|
+
- bundle 组合
|
|
528
|
+
- 变量注入
|
|
529
|
+
- 页面可暴露语言集合
|
|
530
|
+
- 供 `seo.provider` / `seo.sitemap_provider` 消费的稳定 SEO 输入引用或策略提示
|
|
531
|
+
|
|
532
|
+
`page_profile` 不允许承担:
|
|
533
|
+
|
|
534
|
+
- 页面业务逻辑
|
|
535
|
+
- 条件分支执行逻辑
|
|
536
|
+
- 动态内容生成逻辑
|
|
537
|
+
- 第二套 fallback 决策
|
|
538
|
+
- 第二套路由裁决逻辑
|
|
539
|
+
- 直接定义 canonical / hreflang / sitemap / robots 的最终真相
|
|
540
|
+
|
|
541
|
+
治理原则:
|
|
542
|
+
|
|
543
|
+
- profile 负责“怎么组合”
|
|
544
|
+
- bundle 负责“内容是什么”
|
|
545
|
+
- override 负责“极少量特化”
|
|
546
|
+
- 任何超出这三者边界的诉求,都应优先回到模板、bundle 或业务代码本身解决
|
|
547
|
+
|
|
548
|
+
SEO 边界说明:
|
|
549
|
+
|
|
550
|
+
- `page_profile` 可以携带稳定 SEO 输入,例如 `route_binding`、`template_id`、`seo_profile_ref`、`indexability_hint`
|
|
551
|
+
- `page_supported_language` 是页面语言暴露范围的唯一页面级真相源
|
|
552
|
+
- 对子包或业务 app 的稳定公开面,也应优先只暴露 `page_profile` / `page_config` 解析入口;不要长期并存 `resolve_page_profile(...)` 与 `resolve_page_supported_language(...)` 两套同构公开 surface
|
|
553
|
+
- 页面最终 SEO 输出的正式 owner 仍然是 SEO owner 及其消费的 `seo.provider` / `seo.sitemap_provider`
|
|
554
|
+
- 若项目启用了业务 SEO 子包,则页面生命周期、索引门槛、canonical 收敛、hreflang 暴露、sitemap 成员资格等治理,必须继续由对应 SEO 子包负责
|
|
555
|
+
|
|
556
|
+
### 7.7 页面 profile 最小示例
|
|
557
|
+
|
|
558
|
+
以下示例只表达正式结构,不表达具体实现细节。
|
|
559
|
+
|
|
560
|
+
示例 `page_profile`:
|
|
561
|
+
|
|
562
|
+
```jsonc
|
|
563
|
+
{
|
|
564
|
+
"profile_id": "example-topic-landing",
|
|
565
|
+
"route_binding": "public.topic_landing",
|
|
566
|
+
"template_id": "topic_landing_v1",
|
|
567
|
+
"page_supported_language": ["en"],
|
|
568
|
+
"shared_bundle_ids": [
|
|
569
|
+
"site_shell.nav",
|
|
570
|
+
"site_shell.footer"
|
|
571
|
+
],
|
|
572
|
+
"page_type_bundle_ids": [
|
|
573
|
+
"example_app.topic_landing",
|
|
574
|
+
"example_app.topic_faq"
|
|
575
|
+
],
|
|
576
|
+
"page_local_override_id": "example-topic-landing",
|
|
577
|
+
"variables": {
|
|
578
|
+
"topic": "example topic",
|
|
579
|
+
"content_type": "guide",
|
|
580
|
+
"source_context": "example"
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
示例 `shared bundle`:
|
|
586
|
+
|
|
587
|
+
```json
|
|
588
|
+
{
|
|
589
|
+
"nav": {
|
|
590
|
+
"home": "Home",
|
|
591
|
+
"pricing": "Pricing"
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
示例 `page-type bundle`:
|
|
597
|
+
|
|
598
|
+
```json
|
|
599
|
+
{
|
|
600
|
+
"hero": {
|
|
601
|
+
"title": "Best {keyword} Translator",
|
|
602
|
+
"subtitle": "Translate {content_type} from {source_language} instantly."
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
示例 `page-local override`:
|
|
608
|
+
|
|
609
|
+
```json
|
|
610
|
+
{
|
|
611
|
+
"hero": {
|
|
612
|
+
"subtitle": "Specialized for Korean comics and webtoon-style layouts."
|
|
613
|
+
},
|
|
614
|
+
"seo_copy": {
|
|
615
|
+
"description": "Translate Korean comics online with high-quality layout support."
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
该示例表达的正式意图是:
|
|
621
|
+
|
|
622
|
+
- 新增关键词页优先新增一个 profile
|
|
623
|
+
- 优先复用共享 bundle 和 page-type bundle
|
|
624
|
+
- 只有在少量文案确需特化时,才启用稀疏 override
|
|
625
|
+
- `page_supported_language = [\"en\"]` 表示该页面只对外暴露英文
|
|
626
|
+
- 其中 `seo_copy` 只表示可被 `seo.provider` 消费的文案输入,不表示页面直接拥有 canonical / hreflang / sitemap 真相
|
|
627
|
+
|
|
628
|
+
### 7.7.1 模块元信息公开面
|
|
629
|
+
|
|
630
|
+
当共享运行时仍需要读取“模块元信息”时,这些信息必须优先来自 `pz_page_center` 的页面域运行时投影,而不是继续把页面职责塞回 `locales/config.json` 或业务 app 私有公开面。
|
|
631
|
+
|
|
632
|
+
正式要求:
|
|
633
|
+
|
|
634
|
+
- 与页面组合无关、但共享运行时需要读取的模块元信息,仅允许包括这类稳定字段:
|
|
635
|
+
- `name`
|
|
636
|
+
- `default_language`
|
|
637
|
+
- `supported_languages`
|
|
638
|
+
- landing builder、site essentials、business SEO owner 等共享消费者,若确需读取模块元信息,应优先消费 `pz_page_center` 已编译运行时投影
|
|
639
|
+
- `page_profile` 仍只负责页面组合、页面翻译消费声明、页面语言事实投影;不得反向承担模块元信息真相源
|
|
640
|
+
|
|
641
|
+
迁移约束:
|
|
642
|
+
|
|
643
|
+
- 在更大范围的模块元数据系统迁移完成前,允许共享层临时保留 `module_config` fallback
|
|
644
|
+
- 该 fallback 只允许作为迁移期兜底,不构成长期正式主链
|
|
645
|
+
- 不允许因为保留 fallback,就重新把 `sections`、`css`、`js`、`session`、`stripe_preconnect` 等页面职责字段放回 `locales/config.json`
|
|
646
|
+
|
|
647
|
+
## 8. 正式 fallback 规则
|
|
648
|
+
|
|
649
|
+
fallback 必须唯一且可预测。
|
|
650
|
+
|
|
651
|
+
建议固定为:
|
|
652
|
+
|
|
653
|
+
1. 当前 bundle 的当前语言
|
|
654
|
+
2. 当前 bundle 的英文基线
|
|
655
|
+
3. 显式依赖的共享 bundle 的当前语言
|
|
656
|
+
4. 显式依赖的共享 bundle 的英文基线
|
|
657
|
+
|
|
658
|
+
禁止:
|
|
659
|
+
|
|
660
|
+
- 跨业务 bundle 任意 fallback
|
|
661
|
+
- 任意 bundle 自动回退到不相关页面的业务文案
|
|
662
|
+
- 通过无限 `base` 兜底掩盖 owner 缺失
|
|
663
|
+
- 通过多层 override 链掩盖 bundle 切分错误
|
|
664
|
+
- 把 fallback 误当作“页面已支持该语言”
|
|
665
|
+
|
|
666
|
+
说明:
|
|
667
|
+
|
|
668
|
+
- fallback 只解决“当前已暴露语言页面如何兜底取词”
|
|
669
|
+
- fallback 不得自动创建额外页面语言版本
|
|
670
|
+
- fallback 不得自动把语言加入切换列表、hreflang 或 sitemap
|
|
671
|
+
|
|
672
|
+
## 9. `base` allowlist 正式规则
|
|
673
|
+
|
|
674
|
+
`base` 必须进入 allowlist 治理。
|
|
675
|
+
|
|
676
|
+
框架层定义的是 allowlist 机制,而不是为所有项目硬编码同一组共享文件名。
|
|
677
|
+
|
|
678
|
+
正式要求:
|
|
679
|
+
|
|
680
|
+
- 每个 owner app 若声明 `base` 共享面,必须在标准资源元数据中显式列出 allowlist
|
|
681
|
+
- allowlist 必须可被 contract test 和 lint 直接读取
|
|
682
|
+
- allowlist 外的新文件或新前缀必须直接失败
|
|
683
|
+
|
|
684
|
+
共享站点壳的典型 allowlist 可以包括:
|
|
685
|
+
|
|
686
|
+
- `nav`
|
|
687
|
+
- `footer`
|
|
688
|
+
- `common`
|
|
689
|
+
|
|
690
|
+
但这类具体名单属于 app 级治理结果,不是 `pz` 基础层的硬编码常量。
|
|
691
|
+
|
|
692
|
+
## 10. 分层落位
|
|
693
|
+
|
|
694
|
+
### 10.1 `pz` 基础层
|
|
695
|
+
|
|
696
|
+
只有在证明正式合同缺位时,才允许在 `pz` 基础层补以下稳定抽象:
|
|
697
|
+
|
|
698
|
+
- locale bundle 元信息如何进入 `resource manifest`
|
|
699
|
+
- bundle dependency / fallback policy 的最小合同
|
|
700
|
+
- runtime 只消费 manifest 的正式约束
|
|
701
|
+
|
|
702
|
+
`pz` 基础层不负责:
|
|
703
|
+
|
|
704
|
+
- 页面级翻译实现
|
|
705
|
+
- 业务 bundle 列表
|
|
706
|
+
- 业务页面 fallback 细节
|
|
707
|
+
|
|
708
|
+
### 10.2 官方扩展层 / 共享子包
|
|
709
|
+
|
|
710
|
+
官方扩展层负责:
|
|
711
|
+
|
|
712
|
+
- 共享 bundle 的 owner 和资源布局
|
|
713
|
+
- 统一 translation facade 的实现
|
|
714
|
+
- 共享首帧 bundle 的稳定注入策略
|
|
715
|
+
- 共享导航、共享 footer、认证、协议页等共享文案面
|
|
716
|
+
|
|
717
|
+
共享实现若需要通用 facade,应落在官方扩展层或共享子包,不得回塞 `pz` core。
|
|
718
|
+
|
|
719
|
+
### 10.3 业务子包
|
|
720
|
+
|
|
721
|
+
业务子包负责:
|
|
722
|
+
|
|
723
|
+
- 页面 bundle 真相源
|
|
724
|
+
- 页面依赖哪些共享 bundle
|
|
725
|
+
- 页面使用哪些 composition profile
|
|
726
|
+
- 页面是否存在受限 page-local override
|
|
727
|
+
- 页面级 FAQ / pricing / home / tools 文案
|
|
728
|
+
- 页面级 SEO 文案输入与结构化内容输入,但最终 SEO 输出仍应通过 `seo.provider` / `seo.sitemap_provider` 暴露
|
|
729
|
+
- 页面级 `page_supported_language` 和 `page_language_alternates`,供站点语言切换与 SEO provider 统一消费
|
|
730
|
+
|
|
731
|
+
### 10.4 宿主项目
|
|
732
|
+
|
|
733
|
+
宿主只负责:
|
|
734
|
+
|
|
735
|
+
- 安装哪些 app
|
|
736
|
+
- 调用通用 host builder
|
|
737
|
+
- 不负责发明第二套 i18n 装配规则
|
|
738
|
+
|
|
739
|
+
## 11. 治理与门禁
|
|
740
|
+
|
|
741
|
+
长期正式方案必须落成程序化门禁,不能只靠人工约定。
|
|
742
|
+
|
|
743
|
+
### 11.1 owner lint
|
|
744
|
+
|
|
745
|
+
必须校验:
|
|
746
|
+
|
|
747
|
+
- 每个 key 只有一个正式 owner
|
|
748
|
+
- 共享目录中不得出现页面级业务 key
|
|
749
|
+
|
|
750
|
+
### 11.2 allowlist lint
|
|
751
|
+
|
|
752
|
+
必须校验:
|
|
753
|
+
|
|
754
|
+
- `base` 只允许出现 allowlist 文件
|
|
755
|
+
- allowlist 外的新文件或新前缀直接失败
|
|
756
|
+
|
|
757
|
+
### 11.3 parity contract
|
|
758
|
+
|
|
759
|
+
必须校验:
|
|
760
|
+
|
|
761
|
+
- 非英文语言 key 集合与英文基线一致
|
|
762
|
+
- 缺键、重名、owner 冲突都必须显式报错
|
|
763
|
+
|
|
764
|
+
### 11.4 bundle budget
|
|
765
|
+
|
|
766
|
+
必须校验:
|
|
767
|
+
|
|
768
|
+
- 每个 bundle 的体积预算
|
|
769
|
+
- 每个页面 SSR 注入的翻译预算
|
|
770
|
+
- 前端单次加载的翻译预算
|
|
771
|
+
- page-local override 的体积预算
|
|
772
|
+
|
|
773
|
+
### 11.5 dependency contract
|
|
774
|
+
|
|
775
|
+
必须校验:
|
|
776
|
+
|
|
777
|
+
- 页面只能请求自己声明的 bundle
|
|
778
|
+
- 未声明 bundle 的引用直接失败
|
|
779
|
+
- 未出现在 `page_supported_language` 中的语言,不得被页面切换器、hreflang 或 sitemap 暴露
|
|
780
|
+
|
|
781
|
+
### 11.6 override contract
|
|
782
|
+
|
|
783
|
+
必须校验:
|
|
784
|
+
|
|
785
|
+
- override 只能绑定到显式 profile
|
|
786
|
+
- override 只能覆盖白名单字段
|
|
787
|
+
- override 不能成为整页全文案副本
|
|
788
|
+
- 每个 key 的最终来源必须可调试、可追踪
|
|
789
|
+
|
|
790
|
+
### 11.6.1 override 升级为 bundle 的判定规则
|
|
791
|
+
|
|
792
|
+
以下情况出现时,必须优先重构为新的 `page-type bundle`,而不是继续扩张 override:
|
|
793
|
+
|
|
794
|
+
- 多个 profile 重复覆盖同一批字段
|
|
795
|
+
- 同一 override 的字段数量持续增长
|
|
796
|
+
- 同一 override 的体积接近 page-type bundle 规模
|
|
797
|
+
- override 中开始出现结构性文案块,而不是稀疏 patch
|
|
798
|
+
|
|
799
|
+
正式要求:
|
|
800
|
+
|
|
801
|
+
- override 是例外能力,不是抽象承载层
|
|
802
|
+
- 当 override 呈现出“可复用模式”时,必须提升为 bundle
|
|
803
|
+
- 治理门禁应能发现这种演化并阻止继续堆积 patch
|
|
804
|
+
|
|
805
|
+
### 11.7 observability contract
|
|
806
|
+
|
|
807
|
+
必须校验:
|
|
808
|
+
|
|
809
|
+
- 运行时能够输出当前页面实际命中的:
|
|
810
|
+
- profile
|
|
811
|
+
- bundle 列表
|
|
812
|
+
- override
|
|
813
|
+
- 最终语言
|
|
814
|
+
- 每个 key 的最终来源可被调试面或诊断日志追踪
|
|
815
|
+
- 当出现缺键、owner 冲突、非法 override 时,诊断信息必须直接指向正式 owner 和正式资源来源
|
|
816
|
+
|
|
817
|
+
推荐调试能力:
|
|
818
|
+
|
|
819
|
+
- 页面级 locale debug surface
|
|
820
|
+
- key source trace
|
|
821
|
+
- 当前响应的翻译 payload 大小统计
|
|
822
|
+
|
|
823
|
+
### 11.8 performance contract
|
|
824
|
+
|
|
825
|
+
必须校验:
|
|
826
|
+
|
|
827
|
+
- 首帧 SSR 注入的翻译体积受预算限制
|
|
828
|
+
- CSR 单次拉取的 bundle 数量和体积受预算限制
|
|
829
|
+
- profile 组合不会把共享 bundle 重复展开为多份大对象
|
|
830
|
+
- 大规模 SEO 页面场景下,新增 profile 不会导致请求期复杂度线性增加
|
|
831
|
+
- 可缓存公开页面不应在首屏渲染路径上依赖前端翻译请求才能形成完整正文
|
|
832
|
+
- 上述页面若仍存在前端首屏翻译请求,必须能明确说明其不是 SEO/public 首屏正文所必需
|
|
833
|
+
|
|
834
|
+
生产目标:
|
|
835
|
+
|
|
836
|
+
- 请求期复杂度主要与已编译 bundle 个数相关,而不是与页面总数相关
|
|
837
|
+
- 新增关键词页主要增加静态资源与索引项,不应显著增加请求期 CPU 开销
|
|
838
|
+
|
|
839
|
+
### 11.8.1 SEO 协同边界:生命周期与内容质量门槛
|
|
840
|
+
|
|
841
|
+
本规范不单独再定义第二套 SEO 生命周期中心。
|
|
842
|
+
|
|
843
|
+
正式边界:
|
|
844
|
+
|
|
845
|
+
- canonical / robots / hreflang / sitemap / structured data 的最终输出,正式 owner 是 SEO owner 与其 provider chain
|
|
846
|
+
- 页面 app 若需要页面级 SEO 真相,必须通过 `seo.provider` / `seo.sitemap_provider` 暴露,而不是让 `page_profile` 直接接管最终 SEO 输出
|
|
847
|
+
- 若项目启用了业务 SEO 子包,则索引门槛、canonical 收敛、sitemap 成员资格、可内链资格、退场动作等治理,应继续由该子包负责
|
|
848
|
+
- 但页面语言暴露范围的唯一页面级真相源,仍应来自 `page_supported_language`
|
|
849
|
+
- 页面语言切换 URL 的唯一真相源来自同一页面合同的 `page_language_alternates`;缺省时只允许消费 SEO hreflang 同合同投影或当前页面事实兜底,不得自动扩展到全站语言全集
|
|
850
|
+
|
|
851
|
+
`page_profile` 在 SEO 协同中的正式职责仅限于:
|
|
852
|
+
|
|
853
|
+
- 提供稳定页面身份与 route 绑定
|
|
854
|
+
- 提供模板身份与可复用变量
|
|
855
|
+
- 提供页面级语言事实投影
|
|
856
|
+
- 提供可被 provider 消费的 SEO 输入引用,例如 `seo_profile_ref`、`indexability_hint`
|
|
857
|
+
- 不直接充当 SEO provider 或 sitemap provider
|
|
858
|
+
|
|
859
|
+
协同约束:
|
|
860
|
+
|
|
861
|
+
- 仅靠关键词替换、缺乏足够独特内容的页面,不应默认进入可索引状态
|
|
862
|
+
- fallback 比例过高的 locale 页面,不应自动视为可索引页面
|
|
863
|
+
- 若页面长期依赖大体积 override 才成立,应优先判定为 bundle/profile 设计失衡
|
|
864
|
+
- 若项目已有正式 SEO 质量门槛实现,应复用既有门槛,不得在 i18n/profile 层再复制一套判定逻辑
|
|
865
|
+
- 若某页面只支持一个语言,则 hreflang、sitemap 与语言切换器都应只暴露该语言;不得用 `routing.all_supported_languages` 自动扩展当前页面语言切换选项
|
|
866
|
+
|
|
867
|
+
### 11.9 release acceptance contract
|
|
868
|
+
|
|
869
|
+
正式上线前至少应满足:
|
|
870
|
+
|
|
871
|
+
- resource compiler 已能稳定输出 locale bundle / profile / override 的编译结果
|
|
872
|
+
- 首帧 SSR 不再注入整站翻译全集
|
|
873
|
+
- CSR 不再暴露整语言全集加载入口
|
|
874
|
+
- SEO keyword page 的新增流程已验证可通过 profile 组合完成
|
|
875
|
+
- `page_supported_language` / `page_language_alternates` 已被语言切换、`seo.provider`、`seo.sitemap_provider` 一致消费
|
|
876
|
+
- 缺键、owner 冲突、非法 override、非法 dependency 都有明确失败路径
|
|
877
|
+
- 诊断面可以追踪 key 最终来源与 payload 体积
|
|
878
|
+
|
|
879
|
+
## 12. 迁移策略
|
|
880
|
+
|
|
881
|
+
### 12.1 第一阶段:冻结扩张
|
|
882
|
+
|
|
883
|
+
- 停止向 `base` 新增业务页面 key
|
|
884
|
+
- 后续新增翻译必须先判 owner
|
|
885
|
+
|
|
886
|
+
### 12.2 第二阶段:收敛 `base`
|
|
887
|
+
|
|
888
|
+
对存量项目,优先收敛那些历史上误落到 `base`、但实际并非共享稳定文案面的目录。
|
|
889
|
+
|
|
890
|
+
常见类型包括:
|
|
891
|
+
|
|
892
|
+
- 页面级 SEO
|
|
893
|
+
- 业务 dashboard
|
|
894
|
+
- 业务 share
|
|
895
|
+
|
|
896
|
+
原则:
|
|
897
|
+
|
|
898
|
+
- 共享 owner 则迁共享子包
|
|
899
|
+
- 业务 owner 则迁业务子包
|
|
900
|
+
|
|
901
|
+
### 12.3 第三阶段:引入 bundle 消费
|
|
902
|
+
|
|
903
|
+
- 页面和前端模块开始按 bundle 读取翻译
|
|
904
|
+
- 停止“整包语言全集”下发
|
|
905
|
+
- page-local override 仅在白名单场景下启用
|
|
906
|
+
|
|
907
|
+
### 12.4 第四阶段:补齐治理门禁
|
|
908
|
+
|
|
909
|
+
- allowlist
|
|
910
|
+
- owner lint
|
|
911
|
+
- parity contract
|
|
912
|
+
- bundle budget
|
|
913
|
+
- dependency contract
|
|
914
|
+
- observability contract
|
|
915
|
+
- performance contract
|
|
916
|
+
|
|
917
|
+
### 12.5 第五阶段:完成存量迁移
|
|
918
|
+
|
|
919
|
+
在治理门禁到位后,再逐步迁移历史 key 与历史目录。
|
|
920
|
+
|
|
921
|
+
### 12.6 非兼容声明
|
|
922
|
+
|
|
923
|
+
本轮长期方案对历史设计的正式态度如下:
|
|
924
|
+
|
|
925
|
+
- `locales/config.json` 作为历史遗留,本次直接退出正式主链
|
|
926
|
+
- 不保留对 `locales/config.json` 的长期兼容
|
|
927
|
+
- 历史 `base + namespace + 页面整包 JSON` 只视为迁移态,不得继续扩散
|
|
928
|
+
- 新增页面、新增关键词页、新增翻译功能必须遵循新主链,不得再走旧路径
|
|
929
|
+
|
|
930
|
+
## 13. 反模式清单
|
|
931
|
+
|
|
932
|
+
以下做法都应视为反模式:
|
|
933
|
+
|
|
934
|
+
- 把页面组合配置继续塞回 `locales/`
|
|
935
|
+
- 为了省文件数,把整页文案重新塞进单个超大页面 JSON
|
|
936
|
+
- 把 page-local override 当作常规路径使用
|
|
937
|
+
- 让 profile 承担页面业务逻辑或路由分支逻辑
|
|
938
|
+
- 在宿主侧重新拼一套 locale governance 规则
|
|
939
|
+
- 通过运行时多层 merge 解决 bundle 切分不合理问题
|
|
940
|
+
- 遇到重复页面时优先复制文案,而不是抽象 page-type bundle
|
|
941
|
+
|
|
942
|
+
## 14. 当前结论
|
|
943
|
+
|
|
944
|
+
长期正式方案应当是:
|
|
945
|
+
|
|
946
|
+
- 不是继续优化 `base`
|
|
947
|
+
- 不是再造第二套 i18n 资源系统
|
|
948
|
+
- 而是把 i18n 正式收敛成 `pz` 资源体系中的 owner + bundle + manifest + governance 方案
|
|
949
|
+
|
|
950
|
+
只有这样,才能同时满足:
|
|
951
|
+
|
|
952
|
+
- `pz` 的单一路径原则
|
|
953
|
+
- 资源 manifest 唯一真相源
|
|
954
|
+
- 最小子包边界
|
|
955
|
+
- 按需取用
|
|
956
|
+
- 生产环境下的长期可治理性
|