@pwfabric/runtime 0.0.0-stage → 1.0.0-rc.1
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.
- package/LICENSE +90 -0
- package/README.md +43 -2
- package/dist/a11y/announcer.d.ts +65 -0
- package/dist/a11y/announcer.js +140 -0
- package/dist/a11y/announcer.js.map +1 -0
- package/dist/a11y/contrast.d.ts +54 -0
- package/dist/a11y/contrast.js +93 -0
- package/dist/a11y/contrast.js.map +1 -0
- package/dist/a11y/focus.d.ts +123 -0
- package/dist/a11y/focus.js +282 -0
- package/dist/a11y/focus.js.map +1 -0
- package/dist/a11y/index.d.ts +21 -0
- package/dist/a11y/index.js +18 -0
- package/dist/a11y/index.js.map +1 -0
- package/dist/a11y/keyboard.d.ts +144 -0
- package/dist/a11y/keyboard.js +317 -0
- package/dist/a11y/keyboard.js.map +1 -0
- package/dist/adapters/default-image.d.ts +25 -0
- package/dist/adapters/default-image.js +37 -0
- package/dist/adapters/default-image.js.map +1 -0
- package/dist/adapters/index.d.ts +27 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/next-image.d.ts +38 -0
- package/dist/adapters/next-image.js +124 -0
- package/dist/adapters/next-image.js.map +1 -0
- package/dist/adapters/types.d.ts +61 -0
- package/dist/adapters/types.js +11 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/analytics/index.d.ts +14 -0
- package/dist/analytics/index.js +15 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/tracker.d.ts +65 -0
- package/dist/analytics/tracker.js +251 -0
- package/dist/analytics/tracker.js.map +1 -0
- package/dist/analytics/types.d.ts +130 -0
- package/dist/analytics/types.js +11 -0
- package/dist/analytics/types.js.map +1 -0
- package/dist/auth/index.d.ts +15 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/provider.d.ts +132 -0
- package/dist/auth/provider.js +473 -0
- package/dist/auth/provider.js.map +1 -0
- package/dist/auth/types.d.ts +117 -0
- package/dist/auth/types.js +9 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/binding/collection.d.ts +190 -0
- package/dist/binding/collection.js +358 -0
- package/dist/binding/collection.js.map +1 -0
- package/dist/binding/datasource.d.ts +319 -0
- package/dist/binding/datasource.js +556 -0
- package/dist/binding/datasource.js.map +1 -0
- package/dist/binding/engine.d.ts +65 -0
- package/dist/binding/engine.js +268 -0
- package/dist/binding/engine.js.map +1 -0
- package/dist/binding/expression.d.ts +150 -0
- package/dist/binding/expression.js +787 -0
- package/dist/binding/expression.js.map +1 -0
- package/dist/binding/index.d.ts +19 -0
- package/dist/binding/index.js +14 -0
- package/dist/binding/index.js.map +1 -0
- package/dist/binding/two-way.d.ts +149 -0
- package/dist/binding/two-way.js +462 -0
- package/dist/binding/two-way.js.map +1 -0
- package/dist/binding/types.d.ts +119 -0
- package/dist/binding/types.js +9 -0
- package/dist/binding/types.js.map +1 -0
- package/dist/blocks/component-types.d.ts +81 -0
- package/dist/blocks/component-types.js +11 -0
- package/dist/blocks/component-types.js.map +1 -0
- package/dist/blocks/composition-rules.d.ts +168 -0
- package/dist/blocks/composition-rules.js +391 -0
- package/dist/blocks/composition-rules.js.map +1 -0
- package/dist/blocks/hooks.d.ts +31 -0
- package/dist/blocks/hooks.js +42 -0
- package/dist/blocks/hooks.js.map +1 -0
- package/dist/blocks/index.d.ts +14 -0
- package/dist/blocks/index.js +23 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/registry.d.ts +84 -0
- package/dist/blocks/registry.js +136 -0
- package/dist/blocks/registry.js.map +1 -0
- package/dist/blocks/rule-engine.d.ts +131 -0
- package/dist/blocks/rule-engine.js +321 -0
- package/dist/blocks/rule-engine.js.map +1 -0
- package/dist/blocks/surface-event-context.d.ts +13 -0
- package/dist/blocks/surface-event-context.js +14 -0
- package/dist/blocks/surface-event-context.js.map +1 -0
- package/dist/blocks/surface-id-context.d.ts +13 -0
- package/dist/blocks/surface-id-context.js +15 -0
- package/dist/blocks/surface-id-context.js.map +1 -0
- package/dist/bus.d.ts +41 -0
- package/dist/bus.js +158 -0
- package/dist/bus.js.map +1 -0
- package/dist/cache/index.d.ts +9 -0
- package/dist/cache/index.js +10 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/memory.d.ts +32 -0
- package/dist/cache/memory.js +138 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/types.d.ts +62 -0
- package/dist/cache/types.js +9 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/capability/index.d.ts +72 -0
- package/dist/capability/index.js +87 -0
- package/dist/capability/index.js.map +1 -0
- package/dist/capability/kernel-capabilities.d.ts +31 -0
- package/dist/capability/kernel-capabilities.js +36 -0
- package/dist/capability/kernel-capabilities.js.map +1 -0
- package/dist/capability/platform-capabilities.d.ts +71 -0
- package/dist/capability/platform-capabilities.js +267 -0
- package/dist/capability/platform-capabilities.js.map +1 -0
- package/dist/capability/type-mapping.d.ts +70 -0
- package/dist/capability/type-mapping.js +107 -0
- package/dist/capability/type-mapping.js.map +1 -0
- package/dist/capability/validation.d.ts +46 -0
- package/dist/capability/validation.js +54 -0
- package/dist/capability/validation.js.map +1 -0
- package/dist/connectors/declared-connector.d.ts +48 -0
- package/dist/connectors/declared-connector.js +333 -0
- package/dist/connectors/declared-connector.js.map +1 -0
- package/dist/connectors/engine-registry.d.ts +50 -0
- package/dist/connectors/engine-registry.js +135 -0
- package/dist/connectors/engine-registry.js.map +1 -0
- package/dist/connectors/index.d.ts +14 -0
- package/dist/connectors/index.js +14 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/module-loader.d.ts +70 -0
- package/dist/connectors/module-loader.js +143 -0
- package/dist/connectors/module-loader.js.map +1 -0
- package/dist/connectors/types.d.ts +174 -0
- package/dist/connectors/types.js +23 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/context.d.ts +27 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/data/DataCapabilityProvider.d.ts +39 -0
- package/dist/data/DataCapabilityProvider.js +58 -0
- package/dist/data/DataCapabilityProvider.js.map +1 -0
- package/dist/data/index.d.ts +17 -0
- package/dist/data/index.js +18 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/manager.d.ts +40 -0
- package/dist/data/manager.js +442 -0
- package/dist/data/manager.js.map +1 -0
- package/dist/data/types.d.ts +218 -0
- package/dist/data/types.js +2 -0
- package/dist/data/types.js.map +1 -0
- package/dist/entity/aggregate.d.ts +8 -0
- package/dist/entity/aggregate.js +24 -0
- package/dist/entity/aggregate.js.map +1 -0
- package/dist/entity/apply-query.d.ts +58 -0
- package/dist/entity/apply-query.js +264 -0
- package/dist/entity/apply-query.js.map +1 -0
- package/dist/entity/behavior.d.ts +94 -0
- package/dist/entity/behavior.js +78 -0
- package/dist/entity/behavior.js.map +1 -0
- package/dist/entity/connector.d.ts +141 -0
- package/dist/entity/connector.js +89 -0
- package/dist/entity/connector.js.map +1 -0
- package/dist/entity/connectors/index.d.ts +12 -0
- package/dist/entity/connectors/index.js +11 -0
- package/dist/entity/connectors/index.js.map +1 -0
- package/dist/entity/connectors/static.d.ts +51 -0
- package/dist/entity/connectors/static.js +245 -0
- package/dist/entity/connectors/static.js.map +1 -0
- package/dist/entity/entity-datasource.d.ts +50 -0
- package/dist/entity/entity-datasource.js +33 -0
- package/dist/entity/entity-datasource.js.map +1 -0
- package/dist/entity/facade.d.ts +70 -0
- package/dist/entity/facade.js +70 -0
- package/dist/entity/facade.js.map +1 -0
- package/dist/entity/http-connector.d.ts +123 -0
- package/dist/entity/http-connector.js +295 -0
- package/dist/entity/http-connector.js.map +1 -0
- package/dist/entity/index.d.ts +38 -0
- package/dist/entity/index.js +31 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/entity/postgres-connector.d.ts +64 -0
- package/dist/entity/postgres-connector.js +239 -0
- package/dist/entity/postgres-connector.js.map +1 -0
- package/dist/entity/registry.d.ts +59 -0
- package/dist/entity/registry.js +90 -0
- package/dist/entity/registry.js.map +1 -0
- package/dist/entity/store.d.ts +66 -0
- package/dist/entity/store.js +349 -0
- package/dist/entity/store.js.map +1 -0
- package/dist/entity/types.d.ts +180 -0
- package/dist/entity/types.js +10 -0
- package/dist/entity/types.js.map +1 -0
- package/dist/execution-context.d.ts +88 -0
- package/dist/execution-context.js +146 -0
- package/dist/execution-context.js.map +1 -0
- package/dist/executor.d.ts +27 -0
- package/dist/executor.js +33 -0
- package/dist/executor.js.map +1 -0
- package/dist/forms/form-entity-bridge.d.ts +94 -0
- package/dist/forms/form-entity-bridge.js +96 -0
- package/dist/forms/form-entity-bridge.js.map +1 -0
- package/dist/forms/form-write-target.d.ts +34 -0
- package/dist/forms/form-write-target.js +81 -0
- package/dist/forms/form-write-target.js.map +1 -0
- package/dist/forms/index.d.ts +22 -0
- package/dist/forms/index.js +25 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/named-control-values.d.ts +27 -0
- package/dist/forms/named-control-values.js +42 -0
- package/dist/forms/named-control-values.js.map +1 -0
- package/dist/forms/processor.d.ts +55 -0
- package/dist/forms/processor.js +154 -0
- package/dist/forms/processor.js.map +1 -0
- package/dist/forms/types.d.ts +174 -0
- package/dist/forms/types.js +33 -0
- package/dist/forms/types.js.map +1 -0
- package/dist/forms/validator.d.ts +61 -0
- package/dist/forms/validator.js +218 -0
- package/dist/forms/validator.js.map +1 -0
- package/dist/graph/index.d.ts +10 -0
- package/dist/graph/index.js +9 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/resolver.d.ts +66 -0
- package/dist/graph/resolver.js +243 -0
- package/dist/graph/resolver.js.map +1 -0
- package/dist/graph/types.d.ts +125 -0
- package/dist/graph/types.js +9 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/hooks/useInstalledAtoms.d.ts +96 -0
- package/dist/hooks/useInstalledAtoms.js +92 -0
- package/dist/hooks/useInstalledAtoms.js.map +1 -0
- package/dist/i18n/index.d.ts +15 -0
- package/dist/i18n/index.js +14 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/translator.d.ts +76 -0
- package/dist/i18n/translator.js +357 -0
- package/dist/i18n/translator.js.map +1 -0
- package/dist/i18n/types.d.ts +93 -0
- package/dist/i18n/types.js +9 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +47 -0
- package/dist/kernel.js +258 -0
- package/dist/kernel.js.map +1 -0
- package/dist/perf/budget.d.ts +92 -0
- package/dist/perf/budget.js +122 -0
- package/dist/perf/budget.js.map +1 -0
- package/dist/perf/index.d.ts +16 -0
- package/dist/perf/index.js +13 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/render-timer.d.ts +90 -0
- package/dist/perf/render-timer.js +136 -0
- package/dist/perf/render-timer.js.map +1 -0
- package/dist/perf/reporter.d.ts +126 -0
- package/dist/perf/reporter.js +241 -0
- package/dist/perf/reporter.js.map +1 -0
- package/dist/perf/web-vitals.d.ts +111 -0
- package/dist/perf/web-vitals.js +268 -0
- package/dist/perf/web-vitals.js.map +1 -0
- package/dist/registry.d.ts +63 -0
- package/dist/registry.js +126 -0
- package/dist/registry.js.map +1 -0
- package/dist/render/BehaviorSurfaceProvider.d.ts +65 -0
- package/dist/render/BehaviorSurfaceProvider.js +185 -0
- package/dist/render/BehaviorSurfaceProvider.js.map +1 -0
- package/dist/render/BlockErrorBoundary.d.ts +26 -0
- package/dist/render/BlockErrorBoundary.js +41 -0
- package/dist/render/BlockErrorBoundary.js.map +1 -0
- package/dist/render/DataBoundSurfaceRenderer.d.ts +78 -0
- package/dist/render/DataBoundSurfaceRenderer.js +998 -0
- package/dist/render/DataBoundSurfaceRenderer.js.map +1 -0
- package/dist/render/DynamicBlockWrapper.d.ts +23 -0
- package/dist/render/DynamicBlockWrapper.js +117 -0
- package/dist/render/DynamicBlockWrapper.js.map +1 -0
- package/dist/render/appearance-resolver.d.ts +50 -0
- package/dist/render/appearance-resolver.js +78 -0
- package/dist/render/appearance-resolver.js.map +1 -0
- package/dist/render/capability-gate.d.ts +69 -0
- package/dist/render/capability-gate.js +94 -0
- package/dist/render/capability-gate.js.map +1 -0
- package/dist/render/conditional-states-css.d.ts +41 -0
- package/dist/render/conditional-states-css.js +82 -0
- package/dist/render/conditional-states-css.js.map +1 -0
- package/dist/render/connections-context.d.ts +31 -0
- package/dist/render/connections-context.js +34 -0
- package/dist/render/connections-context.js.map +1 -0
- package/dist/render/dark-theme-css.d.ts +36 -0
- package/dist/render/dark-theme-css.js +67 -0
- package/dist/render/dark-theme-css.js.map +1 -0
- package/dist/render/data-binding-types.d.ts +191 -0
- package/dist/render/data-binding-types.js +11 -0
- package/dist/render/data-binding-types.js.map +1 -0
- package/dist/render/data-cache.d.ts +30 -0
- package/dist/render/data-cache.js +37 -0
- package/dist/render/data-cache.js.map +1 -0
- package/dist/render/derive-dark-theme.d.ts +17 -0
- package/dist/render/derive-dark-theme.js +18 -0
- package/dist/render/derive-dark-theme.js.map +1 -0
- package/dist/render/dynamic-artifact-context.d.ts +77 -0
- package/dist/render/dynamic-artifact-context.js +89 -0
- package/dist/render/dynamic-artifact-context.js.map +1 -0
- package/dist/render/dynamic-block-loader.d.ts +80 -0
- package/dist/render/dynamic-block-loader.js +197 -0
- package/dist/render/dynamic-block-loader.js.map +1 -0
- package/dist/render/endpoint.d.ts +18 -0
- package/dist/render/endpoint.js +109 -0
- package/dist/render/endpoint.js.map +1 -0
- package/dist/render/entity-facade-context.d.ts +34 -0
- package/dist/render/entity-facade-context.js +37 -0
- package/dist/render/entity-facade-context.js.map +1 -0
- package/dist/render/group-states-css.d.ts +41 -0
- package/dist/render/group-states-css.js +73 -0
- package/dist/render/group-states-css.js.map +1 -0
- package/dist/render/hooks/useSurfaceEvents.d.ts +8 -0
- package/dist/render/hooks/useSurfaceEvents.js +9 -0
- package/dist/render/hooks/useSurfaceEvents.js.map +1 -0
- package/dist/render/hooks/useSurfaceId.d.ts +8 -0
- package/dist/render/hooks/useSurfaceId.js +9 -0
- package/dist/render/hooks/useSurfaceId.js.map +1 -0
- package/dist/render/index.d.ts +48 -0
- package/dist/render/index.js +61 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/interaction-rule-engine.d.ts +12 -0
- package/dist/render/interaction-rule-engine.js +47 -0
- package/dist/render/interaction-rule-engine.js.map +1 -0
- package/dist/render/keyframes-css.d.ts +31 -0
- package/dist/render/keyframes-css.js +44 -0
- package/dist/render/keyframes-css.js.map +1 -0
- package/dist/render/query-state.d.ts +3 -0
- package/dist/render/query-state.js +70 -0
- package/dist/render/query-state.js.map +1 -0
- package/dist/render/renderer.d.ts +44 -0
- package/dist/render/renderer.js +937 -0
- package/dist/render/renderer.js.map +1 -0
- package/dist/render/resolve-expressions.d.ts +37 -0
- package/dist/render/resolve-expressions.js +309 -0
- package/dist/render/resolve-expressions.js.map +1 -0
- package/dist/render/responsive-style-css.d.ts +42 -0
- package/dist/render/responsive-style-css.js +123 -0
- package/dist/render/responsive-style-css.js.map +1 -0
- package/dist/render/responsive.d.ts +49 -0
- package/dist/render/responsive.js +99 -0
- package/dist/render/responsive.js.map +1 -0
- package/dist/render/scoped-css.d.ts +71 -0
- package/dist/render/scoped-css.js +102 -0
- package/dist/render/scoped-css.js.map +1 -0
- package/dist/render/shell-selection.d.ts +69 -0
- package/dist/render/shell-selection.js +117 -0
- package/dist/render/shell-selection.js.map +1 -0
- package/dist/render/states-css.d.ts +32 -0
- package/dist/render/states-css.js +63 -0
- package/dist/render/states-css.js.map +1 -0
- package/dist/render/surface-data-context.d.ts +18 -0
- package/dist/render/surface-data-context.js +21 -0
- package/dist/render/surface-data-context.js.map +1 -0
- package/dist/render/surface-event-bus.d.ts +19 -0
- package/dist/render/surface-event-bus.js +68 -0
- package/dist/render/surface-event-bus.js.map +1 -0
- package/dist/render/surface-event-context.d.ts +11 -0
- package/dist/render/surface-event-context.js +12 -0
- package/dist/render/surface-event-context.js.map +1 -0
- package/dist/render/surface-id-context.d.ts +8 -0
- package/dist/render/surface-id-context.js +9 -0
- package/dist/render/surface-id-context.js.map +1 -0
- package/dist/render/theme-utils.d.ts +14 -0
- package/dist/render/theme-utils.js +15 -0
- package/dist/render/theme-utils.js.map +1 -0
- package/dist/render/types.d.ts +282 -0
- package/dist/render/types.js +9 -0
- package/dist/render/types.js.map +1 -0
- package/dist/render/visibility-css.d.ts +42 -0
- package/dist/render/visibility-css.js +119 -0
- package/dist/render/visibility-css.js.map +1 -0
- package/dist/render/width-scope.d.ts +34 -0
- package/dist/render/width-scope.js +44 -0
- package/dist/render/width-scope.js.map +1 -0
- package/dist/render/write-only-sources.d.ts +33 -0
- package/dist/render/write-only-sources.js +99 -0
- package/dist/render/write-only-sources.js.map +1 -0
- package/dist/routing/index.d.ts +14 -0
- package/dist/routing/index.js +15 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/router.d.ts +42 -0
- package/dist/routing/router.js +218 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/routing/types.d.ts +124 -0
- package/dist/routing/types.js +9 -0
- package/dist/routing/types.js.map +1 -0
- package/dist/seo/generator.d.ts +39 -0
- package/dist/seo/generator.js +236 -0
- package/dist/seo/generator.js.map +1 -0
- package/dist/seo/index.d.ts +14 -0
- package/dist/seo/index.js +15 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/types.d.ts +181 -0
- package/dist/seo/types.js +9 -0
- package/dist/seo/types.js.map +1 -0
- package/dist/state/index.d.ts +15 -0
- package/dist/state/index.js +16 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/manager.d.ts +33 -0
- package/dist/state/manager.js +81 -0
- package/dist/state/manager.js.map +1 -0
- package/dist/state/store.d.ts +40 -0
- package/dist/state/store.js +145 -0
- package/dist/state/store.js.map +1 -0
- package/dist/state/types.d.ts +107 -0
- package/dist/state/types.js +9 -0
- package/dist/state/types.js.map +1 -0
- package/dist/surface/expression.d.ts +65 -0
- package/dist/surface/expression.js +179 -0
- package/dist/surface/expression.js.map +1 -0
- package/dist/surface/index.d.ts +32 -0
- package/dist/surface/index.js +25 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/surface/interpreter.d.ts +96 -0
- package/dist/surface/interpreter.js +231 -0
- package/dist/surface/interpreter.js.map +1 -0
- package/dist/surface/layout.d.ts +54 -0
- package/dist/surface/layout.js +288 -0
- package/dist/surface/layout.js.map +1 -0
- package/dist/surface/output.d.ts +90 -0
- package/dist/surface/output.js +104 -0
- package/dist/surface/output.js.map +1 -0
- package/dist/surface/primitives.d.ts +180 -0
- package/dist/surface/primitives.js +339 -0
- package/dist/surface/primitives.js.map +1 -0
- package/dist/surface/renderer.d.ts +96 -0
- package/dist/surface/renderer.js +190 -0
- package/dist/surface/renderer.js.map +1 -0
- package/dist/surface/schemas.d.ts +181 -0
- package/dist/surface/schemas.js +142 -0
- package/dist/surface/schemas.js.map +1 -0
- package/dist/surface/transforms.d.ts +144 -0
- package/dist/surface/transforms.js +120 -0
- package/dist/surface/transforms.js.map +1 -0
- package/dist/surface/types.d.ts +160 -0
- package/dist/surface/types.js +51 -0
- package/dist/surface/types.js.map +1 -0
- package/dist/surface/validation.d.ts +123 -0
- package/dist/surface/validation.js +147 -0
- package/dist/surface/validation.js.map +1 -0
- package/dist/validator.d.ts +55 -0
- package/dist/validator.js +100 -0
- package/dist/validator.js.map +1 -0
- package/package.json +106 -3
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Capability Implementation
|
|
3
|
+
*
|
|
4
|
+
* Locale resolution, ICU-style string interpolation,
|
|
5
|
+
* pluralization, and number/date formatting
|
|
6
|
+
*
|
|
7
|
+
* @module @pwfabric/runtime/i18n/translator
|
|
8
|
+
*/
|
|
9
|
+
// ============================================================
|
|
10
|
+
// DEFAULT I18N CAPABILITY
|
|
11
|
+
// ============================================================
|
|
12
|
+
/**
|
|
13
|
+
* Default i18n capability implementation
|
|
14
|
+
*/
|
|
15
|
+
export class DefaultI18nCapability {
|
|
16
|
+
locales = new Map();
|
|
17
|
+
catalogs = new Map();
|
|
18
|
+
currentLocale;
|
|
19
|
+
defaultLocale;
|
|
20
|
+
missingKeyHandler;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.defaultLocale = options?.defaultLocale ?? 'en';
|
|
23
|
+
this.currentLocale = this.defaultLocale;
|
|
24
|
+
this.missingKeyHandler = options?.onMissingKey ?? defaultMissingKeyHandler;
|
|
25
|
+
}
|
|
26
|
+
addLocale(locale) {
|
|
27
|
+
this.locales.set(locale.id, locale);
|
|
28
|
+
}
|
|
29
|
+
loadTranslations(localeId, catalog) {
|
|
30
|
+
let existing = this.catalogs.get(localeId);
|
|
31
|
+
if (!existing) {
|
|
32
|
+
existing = new Map();
|
|
33
|
+
this.catalogs.set(localeId, existing);
|
|
34
|
+
}
|
|
35
|
+
for (const [key, value] of Object.entries(catalog)) {
|
|
36
|
+
existing.set(key, value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
resolveLocale(preferred) {
|
|
40
|
+
// Try exact match
|
|
41
|
+
for (const locale of preferred) {
|
|
42
|
+
if (this.catalogs.has(locale)) {
|
|
43
|
+
return locale;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Try language-only match (e.g. 'en-US' -> 'en')
|
|
47
|
+
for (const locale of preferred) {
|
|
48
|
+
const language = locale.split('-')[0];
|
|
49
|
+
if (language && this.catalogs.has(language)) {
|
|
50
|
+
return language;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Try matching registered locales by language prefix
|
|
54
|
+
for (const locale of preferred) {
|
|
55
|
+
const language = locale.split('-')[0];
|
|
56
|
+
if (language) {
|
|
57
|
+
for (const registeredId of this.catalogs.keys()) {
|
|
58
|
+
if (registeredId.startsWith(language)) {
|
|
59
|
+
return registeredId;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return this.defaultLocale;
|
|
65
|
+
}
|
|
66
|
+
setLocale(localeId) {
|
|
67
|
+
this.currentLocale = localeId;
|
|
68
|
+
}
|
|
69
|
+
getLocale() {
|
|
70
|
+
return this.currentLocale;
|
|
71
|
+
}
|
|
72
|
+
getLocaleIds() {
|
|
73
|
+
return Array.from(this.catalogs.keys());
|
|
74
|
+
}
|
|
75
|
+
translate(key, values) {
|
|
76
|
+
const message = this.lookupKey(key, this.currentLocale);
|
|
77
|
+
if (message === undefined) {
|
|
78
|
+
return this.missingKeyHandler(key, this.currentLocale);
|
|
79
|
+
}
|
|
80
|
+
if (!values || Object.keys(values).length === 0) {
|
|
81
|
+
return message;
|
|
82
|
+
}
|
|
83
|
+
return this.interpolate(message, values);
|
|
84
|
+
}
|
|
85
|
+
t(key, values) {
|
|
86
|
+
return this.translate(key, values);
|
|
87
|
+
}
|
|
88
|
+
formatNumber(value, options) {
|
|
89
|
+
try {
|
|
90
|
+
const intlOptions = {};
|
|
91
|
+
if (options?.style)
|
|
92
|
+
intlOptions.style = options.style;
|
|
93
|
+
if (options?.currency)
|
|
94
|
+
intlOptions.currency = options.currency;
|
|
95
|
+
if (options?.minimumFractionDigits !== undefined) {
|
|
96
|
+
intlOptions.minimumFractionDigits = options.minimumFractionDigits;
|
|
97
|
+
}
|
|
98
|
+
if (options?.maximumFractionDigits !== undefined) {
|
|
99
|
+
intlOptions.maximumFractionDigits = options.maximumFractionDigits;
|
|
100
|
+
}
|
|
101
|
+
if (options?.useGrouping !== undefined)
|
|
102
|
+
intlOptions.useGrouping = options.useGrouping;
|
|
103
|
+
return new Intl.NumberFormat(this.currentLocale, intlOptions).format(value);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Fallback to basic formatting
|
|
107
|
+
return String(value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
formatDate(value, options) {
|
|
111
|
+
try {
|
|
112
|
+
const date = typeof value === 'number' ? new Date(value) : value;
|
|
113
|
+
const intlOptions = {};
|
|
114
|
+
if (options?.dateStyle)
|
|
115
|
+
intlOptions.dateStyle = options.dateStyle;
|
|
116
|
+
if (options?.timeStyle)
|
|
117
|
+
intlOptions.timeStyle = options.timeStyle;
|
|
118
|
+
if (options?.year)
|
|
119
|
+
intlOptions.year = options.year;
|
|
120
|
+
if (options?.month)
|
|
121
|
+
intlOptions.month = options.month;
|
|
122
|
+
if (options?.day)
|
|
123
|
+
intlOptions.day = options.day;
|
|
124
|
+
if (options?.hour)
|
|
125
|
+
intlOptions.hour = options.hour;
|
|
126
|
+
if (options?.minute)
|
|
127
|
+
intlOptions.minute = options.minute;
|
|
128
|
+
if (options?.second)
|
|
129
|
+
intlOptions.second = options.second;
|
|
130
|
+
if (options?.timeZone)
|
|
131
|
+
intlOptions.timeZone = options.timeZone;
|
|
132
|
+
return new Intl.DateTimeFormat(this.currentLocale, intlOptions).format(date);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Fallback to ISO string
|
|
136
|
+
const date = typeof value === 'number' ? new Date(value) : value;
|
|
137
|
+
return date.toISOString();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
hasTranslation(key) {
|
|
141
|
+
return this.lookupKey(key, this.currentLocale) !== undefined;
|
|
142
|
+
}
|
|
143
|
+
clear() {
|
|
144
|
+
this.locales.clear();
|
|
145
|
+
this.catalogs.clear();
|
|
146
|
+
this.currentLocale = this.defaultLocale;
|
|
147
|
+
}
|
|
148
|
+
// ============================================================
|
|
149
|
+
// PRIVATE METHODS
|
|
150
|
+
// ============================================================
|
|
151
|
+
lookupKey(key, localeId) {
|
|
152
|
+
// Try current locale
|
|
153
|
+
const catalog = this.catalogs.get(localeId);
|
|
154
|
+
if (catalog?.has(key)) {
|
|
155
|
+
return catalog.get(key);
|
|
156
|
+
}
|
|
157
|
+
// Try fallback chain
|
|
158
|
+
const locale = this.locales.get(localeId);
|
|
159
|
+
if (locale?.fallback) {
|
|
160
|
+
return this.lookupKey(key, locale.fallback);
|
|
161
|
+
}
|
|
162
|
+
// Try default locale if different
|
|
163
|
+
if (localeId !== this.defaultLocale) {
|
|
164
|
+
const defaultCatalog = this.catalogs.get(this.defaultLocale);
|
|
165
|
+
if (defaultCatalog?.has(key)) {
|
|
166
|
+
return defaultCatalog.get(key);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Interpolate ICU-style message patterns
|
|
173
|
+
*
|
|
174
|
+
* Supports:
|
|
175
|
+
* - Simple: "Hello, {name}!"
|
|
176
|
+
* - Plural: "{count, plural, one {# item} other {# items}}"
|
|
177
|
+
* - Select: "{gender, select, male {He} female {She} other {They}}"
|
|
178
|
+
*/
|
|
179
|
+
interpolate(message, values) {
|
|
180
|
+
let result = '';
|
|
181
|
+
let i = 0;
|
|
182
|
+
while (i < message.length) {
|
|
183
|
+
if (message[i] === '{') {
|
|
184
|
+
// Extract the full brace-balanced block
|
|
185
|
+
const block = this.extractBraceBlock(message, i);
|
|
186
|
+
if (block !== null) {
|
|
187
|
+
const inner = block.content.trim();
|
|
188
|
+
result += this.resolveBlock(inner, values);
|
|
189
|
+
i = block.end + 1;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
result += message[i];
|
|
193
|
+
i++;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
result += message[i];
|
|
198
|
+
i++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Extract a brace-balanced block starting at position `start`
|
|
205
|
+
* Returns the content between the outer braces and the end index
|
|
206
|
+
*/
|
|
207
|
+
extractBraceBlock(message, start) {
|
|
208
|
+
if (message[start] !== '{')
|
|
209
|
+
return null;
|
|
210
|
+
let depth = 0;
|
|
211
|
+
let i = start;
|
|
212
|
+
while (i < message.length) {
|
|
213
|
+
if (message[i] === '{') {
|
|
214
|
+
depth++;
|
|
215
|
+
}
|
|
216
|
+
else if (message[i] === '}') {
|
|
217
|
+
depth--;
|
|
218
|
+
if (depth === 0) {
|
|
219
|
+
return {
|
|
220
|
+
content: message.slice(start + 1, i),
|
|
221
|
+
end: i,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
i++;
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Resolve a single interpolation block (content between outer braces)
|
|
231
|
+
*/
|
|
232
|
+
resolveBlock(inner, values) {
|
|
233
|
+
// Check for plural/select: "name, plural, ..." or "name, select, ..."
|
|
234
|
+
const commaIndex = inner.indexOf(',');
|
|
235
|
+
if (commaIndex > 0) {
|
|
236
|
+
const varName = inner.slice(0, commaIndex).trim();
|
|
237
|
+
const rest = inner.slice(commaIndex + 1).trim();
|
|
238
|
+
// Check for plural
|
|
239
|
+
if (rest.startsWith('plural,')) {
|
|
240
|
+
const pluralBody = rest.slice('plural,'.length).trim();
|
|
241
|
+
const value = values[varName];
|
|
242
|
+
if (typeof value === 'number') {
|
|
243
|
+
return this.resolvePlural(value, pluralBody);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Check for select
|
|
247
|
+
if (rest.startsWith('select,')) {
|
|
248
|
+
const selectBody = rest.slice('select,'.length).trim();
|
|
249
|
+
const value = values[varName];
|
|
250
|
+
return this.resolveSelect(String(value), selectBody);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Simple interpolation: "name"
|
|
254
|
+
const value = values[inner];
|
|
255
|
+
if (value !== undefined) {
|
|
256
|
+
return String(value);
|
|
257
|
+
}
|
|
258
|
+
return `{${inner}}`;
|
|
259
|
+
}
|
|
260
|
+
resolvePlural(count, body) {
|
|
261
|
+
const category = this.getPluralCategory(count);
|
|
262
|
+
const options = this.parseICUOptions(body);
|
|
263
|
+
// Try exact match first (=0, =1, etc.)
|
|
264
|
+
const exactMatch = options.get(`=${count}`);
|
|
265
|
+
if (exactMatch) {
|
|
266
|
+
return exactMatch.replace(/#/g, String(count));
|
|
267
|
+
}
|
|
268
|
+
// Try category match
|
|
269
|
+
const categoryMatch = options.get(category);
|
|
270
|
+
if (categoryMatch) {
|
|
271
|
+
return categoryMatch.replace(/#/g, String(count));
|
|
272
|
+
}
|
|
273
|
+
// Fallback to 'other'
|
|
274
|
+
const other = options.get('other');
|
|
275
|
+
if (other) {
|
|
276
|
+
return other.replace(/#/g, String(count));
|
|
277
|
+
}
|
|
278
|
+
return String(count);
|
|
279
|
+
}
|
|
280
|
+
resolveSelect(value, body) {
|
|
281
|
+
const options = this.parseICUOptions(body);
|
|
282
|
+
const match = options.get(value);
|
|
283
|
+
if (match)
|
|
284
|
+
return match;
|
|
285
|
+
const other = options.get('other');
|
|
286
|
+
if (other)
|
|
287
|
+
return other;
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Parse ICU option blocks like: "one {# item} other {# items}"
|
|
292
|
+
* Handles nested braces correctly using a brace-depth parser.
|
|
293
|
+
*/
|
|
294
|
+
parseICUOptions(body) {
|
|
295
|
+
const options = new Map();
|
|
296
|
+
let i = 0;
|
|
297
|
+
while (i < body.length) {
|
|
298
|
+
// Skip whitespace
|
|
299
|
+
while (i < body.length && /\s/.test(body[i]))
|
|
300
|
+
i++;
|
|
301
|
+
if (i >= body.length)
|
|
302
|
+
break;
|
|
303
|
+
// Read key (non-whitespace until { or whitespace)
|
|
304
|
+
let key = '';
|
|
305
|
+
while (i < body.length && body[i] !== '{' && !/\s/.test(body[i])) {
|
|
306
|
+
key += body[i];
|
|
307
|
+
i++;
|
|
308
|
+
}
|
|
309
|
+
if (!key)
|
|
310
|
+
break;
|
|
311
|
+
// Skip whitespace before opening brace
|
|
312
|
+
while (i < body.length && /\s/.test(body[i]))
|
|
313
|
+
i++;
|
|
314
|
+
if (i >= body.length || body[i] !== '{')
|
|
315
|
+
break;
|
|
316
|
+
// Extract brace-balanced value
|
|
317
|
+
const block = this.extractBraceBlock(body, i);
|
|
318
|
+
if (block === null)
|
|
319
|
+
break;
|
|
320
|
+
options.set(key, block.content);
|
|
321
|
+
i = block.end + 1;
|
|
322
|
+
}
|
|
323
|
+
return options;
|
|
324
|
+
}
|
|
325
|
+
getPluralCategory(count) {
|
|
326
|
+
// Basic English-style plural rules
|
|
327
|
+
// For production use, this should use Intl.PluralRules
|
|
328
|
+
try {
|
|
329
|
+
const pr = new Intl.PluralRules(this.currentLocale);
|
|
330
|
+
return pr.select(count);
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// Fallback: simple English plural
|
|
334
|
+
if (count === 0)
|
|
335
|
+
return 'zero';
|
|
336
|
+
if (count === 1)
|
|
337
|
+
return 'one';
|
|
338
|
+
return 'other';
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Default handler returns the key itself as a visible fallback
|
|
344
|
+
*/
|
|
345
|
+
function defaultMissingKeyHandler(key, _locale) {
|
|
346
|
+
return key;
|
|
347
|
+
}
|
|
348
|
+
// ============================================================
|
|
349
|
+
// FACTORY FUNCTION
|
|
350
|
+
// ============================================================
|
|
351
|
+
/**
|
|
352
|
+
* Create an i18n capability instance
|
|
353
|
+
*/
|
|
354
|
+
export function createI18nCapability(options) {
|
|
355
|
+
return new DefaultI18nCapability(options);
|
|
356
|
+
}
|
|
357
|
+
//# sourceMappingURL=translator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translator.js","sourceRoot":"","sources":["../../src/i18n/translator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * i18n Capability Implementation\n *\n * Locale resolution, ICU-style string interpolation,\n * pluralization, and number/date formatting\n *\n * @module @pwfabric/runtime/i18n/translator\n */\n\nimport type {\n I18nCapability,\n LocaleId,\n LocaleDefinition,\n TranslationCatalog,\n PluralCategory,\n NumberFormatOptions,\n DateFormatOptions,\n} from './types.js'\n\n// ============================================================\n// DEFAULT I18N CAPABILITY\n// ============================================================\n\n/**\n * Default i18n capability implementation\n */\nexport class DefaultI18nCapability implements I18nCapability {\n private readonly locales = new Map<LocaleId, LocaleDefinition>()\n private readonly catalogs = new Map<LocaleId, Map<string, string>>()\n private currentLocale: LocaleId\n private readonly defaultLocale: LocaleId\n private readonly missingKeyHandler: MissingKeyHandler\n\n constructor(options?: I18nOptions) {\n this.defaultLocale = options?.defaultLocale ?? 'en'\n this.currentLocale = this.defaultLocale\n this.missingKeyHandler = options?.onMissingKey ?? defaultMissingKeyHandler\n }\n\n addLocale(locale: LocaleDefinition): void {\n this.locales.set(locale.id, locale)\n }\n\n loadTranslations(localeId: LocaleId, catalog: TranslationCatalog): void {\n let existing = this.catalogs.get(localeId)\n if (!existing) {\n existing = new Map()\n this.catalogs.set(localeId, existing)\n }\n\n for (const [key, value] of Object.entries(catalog)) {\n existing.set(key, value)\n }\n }\n\n resolveLocale(preferred: readonly LocaleId[]): LocaleId {\n // Try exact match\n for (const locale of preferred) {\n if (this.catalogs.has(locale)) {\n return locale\n }\n }\n\n // Try language-only match (e.g. 'en-US' -> 'en')\n for (const locale of preferred) {\n const language = locale.split('-')[0]\n if (language && this.catalogs.has(language)) {\n return language\n }\n }\n\n // Try matching registered locales by language prefix\n for (const locale of preferred) {\n const language = locale.split('-')[0]\n if (language) {\n for (const registeredId of this.catalogs.keys()) {\n if (registeredId.startsWith(language)) {\n return registeredId\n }\n }\n }\n }\n\n return this.defaultLocale\n }\n\n setLocale(localeId: LocaleId): void {\n this.currentLocale = localeId\n }\n\n getLocale(): LocaleId {\n return this.currentLocale\n }\n\n getLocaleIds(): readonly LocaleId[] {\n return Array.from(this.catalogs.keys())\n }\n\n translate(key: string, values?: Readonly<Record<string, unknown>>): string {\n const message = this.lookupKey(key, this.currentLocale)\n\n if (message === undefined) {\n return this.missingKeyHandler(key, this.currentLocale)\n }\n\n if (!values || Object.keys(values).length === 0) {\n return message\n }\n\n return this.interpolate(message, values)\n }\n\n t(key: string, values?: Readonly<Record<string, unknown>>): string {\n return this.translate(key, values)\n }\n\n formatNumber(value: number, options?: NumberFormatOptions): string {\n try {\n const intlOptions: Intl.NumberFormatOptions = {}\n\n if (options?.style) intlOptions.style = options.style\n if (options?.currency) intlOptions.currency = options.currency\n if (options?.minimumFractionDigits !== undefined) {\n intlOptions.minimumFractionDigits = options.minimumFractionDigits\n }\n if (options?.maximumFractionDigits !== undefined) {\n intlOptions.maximumFractionDigits = options.maximumFractionDigits\n }\n if (options?.useGrouping !== undefined) intlOptions.useGrouping = options.useGrouping\n\n return new Intl.NumberFormat(this.currentLocale, intlOptions).format(value)\n } catch {\n // Fallback to basic formatting\n return String(value)\n }\n }\n\n formatDate(value: Date | number, options?: DateFormatOptions): string {\n try {\n const date = typeof value === 'number' ? new Date(value) : value\n const intlOptions: Intl.DateTimeFormatOptions = {}\n\n if (options?.dateStyle) intlOptions.dateStyle = options.dateStyle\n if (options?.timeStyle) intlOptions.timeStyle = options.timeStyle\n if (options?.year) intlOptions.year = options.year\n if (options?.month) intlOptions.month = options.month\n if (options?.day) intlOptions.day = options.day\n if (options?.hour) intlOptions.hour = options.hour\n if (options?.minute) intlOptions.minute = options.minute\n if (options?.second) intlOptions.second = options.second\n if (options?.timeZone) intlOptions.timeZone = options.timeZone\n\n return new Intl.DateTimeFormat(this.currentLocale, intlOptions).format(date)\n } catch {\n // Fallback to ISO string\n const date = typeof value === 'number' ? new Date(value) : value\n return date.toISOString()\n }\n }\n\n hasTranslation(key: string): boolean {\n return this.lookupKey(key, this.currentLocale) !== undefined\n }\n\n clear(): void {\n this.locales.clear()\n this.catalogs.clear()\n this.currentLocale = this.defaultLocale\n }\n\n // ============================================================\n // PRIVATE METHODS\n // ============================================================\n\n private lookupKey(key: string, localeId: LocaleId): string | undefined {\n // Try current locale\n const catalog = this.catalogs.get(localeId)\n if (catalog?.has(key)) {\n return catalog.get(key)\n }\n\n // Try fallback chain\n const locale = this.locales.get(localeId)\n if (locale?.fallback) {\n return this.lookupKey(key, locale.fallback)\n }\n\n // Try default locale if different\n if (localeId !== this.defaultLocale) {\n const defaultCatalog = this.catalogs.get(this.defaultLocale)\n if (defaultCatalog?.has(key)) {\n return defaultCatalog.get(key)\n }\n }\n\n return undefined\n }\n\n /**\n * Interpolate ICU-style message patterns\n *\n * Supports:\n * - Simple: \"Hello, {name}!\"\n * - Plural: \"{count, plural, one {# item} other {# items}}\"\n * - Select: \"{gender, select, male {He} female {She} other {They}}\"\n */\n private interpolate(message: string, values: Readonly<Record<string, unknown>>): string {\n let result = ''\n let i = 0\n\n while (i < message.length) {\n if (message[i] === '{') {\n // Extract the full brace-balanced block\n const block = this.extractBraceBlock(message, i)\n if (block !== null) {\n const inner = block.content.trim()\n result += this.resolveBlock(inner, values)\n i = block.end + 1\n } else {\n result += message[i]\n i++\n }\n } else {\n result += message[i]\n i++\n }\n }\n\n return result\n }\n\n /**\n * Extract a brace-balanced block starting at position `start`\n * Returns the content between the outer braces and the end index\n */\n private extractBraceBlock(\n message: string,\n start: number\n ): { content: string; end: number } | null {\n if (message[start] !== '{') return null\n\n let depth = 0\n let i = start\n\n while (i < message.length) {\n if (message[i] === '{') {\n depth++\n } else if (message[i] === '}') {\n depth--\n if (depth === 0) {\n return {\n content: message.slice(start + 1, i),\n end: i,\n }\n }\n }\n i++\n }\n\n return null\n }\n\n /**\n * Resolve a single interpolation block (content between outer braces)\n */\n private resolveBlock(inner: string, values: Readonly<Record<string, unknown>>): string {\n // Check for plural/select: \"name, plural, ...\" or \"name, select, ...\"\n const commaIndex = inner.indexOf(',')\n if (commaIndex > 0) {\n const varName = inner.slice(0, commaIndex).trim()\n const rest = inner.slice(commaIndex + 1).trim()\n\n // Check for plural\n if (rest.startsWith('plural,')) {\n const pluralBody = rest.slice('plural,'.length).trim()\n const value = values[varName]\n if (typeof value === 'number') {\n return this.resolvePlural(value, pluralBody)\n }\n }\n\n // Check for select\n if (rest.startsWith('select,')) {\n const selectBody = rest.slice('select,'.length).trim()\n const value = values[varName]\n return this.resolveSelect(String(value), selectBody)\n }\n }\n\n // Simple interpolation: \"name\"\n const value = values[inner]\n if (value !== undefined) {\n return String(value)\n }\n\n return `{${inner}}`\n }\n\n private resolvePlural(count: number, body: string): string {\n const category = this.getPluralCategory(count)\n const options = this.parseICUOptions(body)\n\n // Try exact match first (=0, =1, etc.)\n const exactMatch = options.get(`=${count}`)\n if (exactMatch) {\n return exactMatch.replace(/#/g, String(count))\n }\n\n // Try category match\n const categoryMatch = options.get(category)\n if (categoryMatch) {\n return categoryMatch.replace(/#/g, String(count))\n }\n\n // Fallback to 'other'\n const other = options.get('other')\n if (other) {\n return other.replace(/#/g, String(count))\n }\n\n return String(count)\n }\n\n private resolveSelect(value: string, body: string): string {\n const options = this.parseICUOptions(body)\n\n const match = options.get(value)\n if (match) return match\n\n const other = options.get('other')\n if (other) return other\n\n return value\n }\n\n /**\n * Parse ICU option blocks like: \"one {# item} other {# items}\"\n * Handles nested braces correctly using a brace-depth parser.\n */\n private parseICUOptions(body: string): Map<string, string> {\n const options = new Map<string, string>()\n let i = 0\n\n while (i < body.length) {\n // Skip whitespace\n while (i < body.length && /\\s/.test(body[i] as string)) i++\n if (i >= body.length) break\n\n // Read key (non-whitespace until { or whitespace)\n let key = ''\n while (i < body.length && body[i] !== '{' && !/\\s/.test(body[i] as string)) {\n key += body[i]\n i++\n }\n if (!key) break\n\n // Skip whitespace before opening brace\n while (i < body.length && /\\s/.test(body[i] as string)) i++\n if (i >= body.length || body[i] !== '{') break\n\n // Extract brace-balanced value\n const block = this.extractBraceBlock(body, i)\n if (block === null) break\n\n options.set(key, block.content)\n i = block.end + 1\n }\n\n return options\n }\n\n private getPluralCategory(count: number): PluralCategory {\n // Basic English-style plural rules\n // For production use, this should use Intl.PluralRules\n try {\n const pr = new Intl.PluralRules(this.currentLocale)\n return pr.select(count) as PluralCategory\n } catch {\n // Fallback: simple English plural\n if (count === 0) return 'zero'\n if (count === 1) return 'one'\n return 'other'\n }\n }\n}\n\n// ============================================================\n// TYPES AND HELPERS\n// ============================================================\n\n/**\n * i18n construction options\n */\nexport interface I18nOptions {\n /** Default locale to use */\n readonly defaultLocale?: LocaleId\n\n /** Handler for missing translation keys */\n readonly onMissingKey?: MissingKeyHandler\n}\n\n/**\n * Handler called when a translation key is not found\n */\nexport type MissingKeyHandler = (key: string, locale: LocaleId) => string\n\n/**\n * Default handler returns the key itself as a visible fallback\n */\nfunction defaultMissingKeyHandler(key: string, _locale: LocaleId): string {\n return key\n}\n\n// ============================================================\n// FACTORY FUNCTION\n// ============================================================\n\n/**\n * Create an i18n capability instance\n */\nexport function createI18nCapability(options?: I18nOptions): I18nCapability {\n return new DefaultI18nCapability(options)\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Capability Types
|
|
3
|
+
*
|
|
4
|
+
* Locale resolution, translation, pluralization, and formatting
|
|
5
|
+
*
|
|
6
|
+
* @module @pwfabric/runtime/i18n/types
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Locale identifier (BCP 47 format, e.g. 'en', 'en-US', 'pt-BR')
|
|
10
|
+
*/
|
|
11
|
+
export type LocaleId = string;
|
|
12
|
+
/**
|
|
13
|
+
* Locale definition
|
|
14
|
+
*/
|
|
15
|
+
export interface LocaleDefinition {
|
|
16
|
+
readonly id: LocaleId;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly nativeName?: string;
|
|
19
|
+
readonly direction?: 'ltr' | 'rtl';
|
|
20
|
+
readonly fallback?: LocaleId;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Translation catalog -- flat key-value pairs for a locale
|
|
24
|
+
*
|
|
25
|
+
* Keys can be dot-separated for namespacing (e.g. 'nav.home', 'errors.notFound').
|
|
26
|
+
* Values support ICU-style interpolation:
|
|
27
|
+
* - Simple: "Hello, {name}!"
|
|
28
|
+
* - Plural: "{count, plural, one {# item} other {# items}}"
|
|
29
|
+
* - Select: "{gender, select, male {He} female {She} other {They}}"
|
|
30
|
+
*/
|
|
31
|
+
export type TranslationCatalog = Readonly<Record<string, string>>;
|
|
32
|
+
/**
|
|
33
|
+
* Plural categories (CLDR)
|
|
34
|
+
*/
|
|
35
|
+
export type PluralCategory = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
|
|
36
|
+
/**
|
|
37
|
+
* Plural rule function
|
|
38
|
+
*/
|
|
39
|
+
export type PluralRuleFunction = (count: number) => PluralCategory;
|
|
40
|
+
/**
|
|
41
|
+
* Number format options
|
|
42
|
+
*/
|
|
43
|
+
export interface NumberFormatOptions {
|
|
44
|
+
readonly style?: 'decimal' | 'currency' | 'percent' | 'unit';
|
|
45
|
+
readonly currency?: string;
|
|
46
|
+
readonly minimumFractionDigits?: number;
|
|
47
|
+
readonly maximumFractionDigits?: number;
|
|
48
|
+
readonly useGrouping?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Date format options
|
|
52
|
+
*/
|
|
53
|
+
export interface DateFormatOptions {
|
|
54
|
+
readonly style?: 'short' | 'medium' | 'long' | 'full';
|
|
55
|
+
readonly dateStyle?: 'short' | 'medium' | 'long' | 'full';
|
|
56
|
+
readonly timeStyle?: 'short' | 'medium' | 'long' | 'full';
|
|
57
|
+
readonly year?: 'numeric' | '2-digit';
|
|
58
|
+
readonly month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';
|
|
59
|
+
readonly day?: 'numeric' | '2-digit';
|
|
60
|
+
readonly hour?: 'numeric' | '2-digit';
|
|
61
|
+
readonly minute?: 'numeric' | '2-digit';
|
|
62
|
+
readonly second?: 'numeric' | '2-digit';
|
|
63
|
+
readonly timeZone?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* i18n capability interface
|
|
67
|
+
*/
|
|
68
|
+
export interface I18nCapability {
|
|
69
|
+
/** Register a locale definition */
|
|
70
|
+
addLocale(locale: LocaleDefinition): void;
|
|
71
|
+
/** Load translations for a locale */
|
|
72
|
+
loadTranslations(localeId: LocaleId, catalog: TranslationCatalog): void;
|
|
73
|
+
/** Resolve the best locale from a list of preferred locales */
|
|
74
|
+
resolveLocale(preferred: readonly LocaleId[]): LocaleId;
|
|
75
|
+
/** Set the current active locale */
|
|
76
|
+
setLocale(localeId: LocaleId): void;
|
|
77
|
+
/** Get the current active locale */
|
|
78
|
+
getLocale(): LocaleId;
|
|
79
|
+
/** Get all registered locale IDs */
|
|
80
|
+
getLocaleIds(): readonly LocaleId[];
|
|
81
|
+
/** Translate a key with optional interpolation values */
|
|
82
|
+
translate(key: string, values?: Readonly<Record<string, unknown>>): string;
|
|
83
|
+
/** Shorthand alias for translate */
|
|
84
|
+
t(key: string, values?: Readonly<Record<string, unknown>>): string;
|
|
85
|
+
/** Format a number according to the current locale */
|
|
86
|
+
formatNumber(value: number, options?: NumberFormatOptions): string;
|
|
87
|
+
/** Format a date according to the current locale */
|
|
88
|
+
formatDate(value: Date | number, options?: DateFormatOptions): string;
|
|
89
|
+
/** Check if a translation key exists */
|
|
90
|
+
hasTranslation(key: string): boolean;
|
|
91
|
+
/** Clear all locales and translations */
|
|
92
|
+
clear(): void;
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/i18n/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * i18n Capability Types\n *\n * Locale resolution, translation, pluralization, and formatting\n *\n * @module @pwfabric/runtime/i18n/types\n */\n\n// ============================================================\n// LOCALE\n// ============================================================\n\n/**\n * Locale identifier (BCP 47 format, e.g. 'en', 'en-US', 'pt-BR')\n */\nexport type LocaleId = string\n\n/**\n * Locale definition\n */\nexport interface LocaleDefinition {\n readonly id: LocaleId\n readonly name: string\n readonly nativeName?: string\n readonly direction?: 'ltr' | 'rtl'\n readonly fallback?: LocaleId\n}\n\n// ============================================================\n// TRANSLATION CATALOG\n// ============================================================\n\n/**\n * Translation catalog -- flat key-value pairs for a locale\n *\n * Keys can be dot-separated for namespacing (e.g. 'nav.home', 'errors.notFound').\n * Values support ICU-style interpolation:\n * - Simple: \"Hello, {name}!\"\n * - Plural: \"{count, plural, one {# item} other {# items}}\"\n * - Select: \"{gender, select, male {He} female {She} other {They}}\"\n */\nexport type TranslationCatalog = Readonly<Record<string, string>>\n\n// ============================================================\n// PLURALIZATION\n// ============================================================\n\n/**\n * Plural categories (CLDR)\n */\nexport type PluralCategory = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'\n\n/**\n * Plural rule function\n */\nexport type PluralRuleFunction = (count: number) => PluralCategory\n\n// ============================================================\n// FORMAT OPTIONS\n// ============================================================\n\n/**\n * Number format options\n */\nexport interface NumberFormatOptions {\n readonly style?: 'decimal' | 'currency' | 'percent' | 'unit'\n readonly currency?: string\n readonly minimumFractionDigits?: number\n readonly maximumFractionDigits?: number\n readonly useGrouping?: boolean\n}\n\n/**\n * Date format options\n */\nexport interface DateFormatOptions {\n readonly style?: 'short' | 'medium' | 'long' | 'full'\n readonly dateStyle?: 'short' | 'medium' | 'long' | 'full'\n readonly timeStyle?: 'short' | 'medium' | 'long' | 'full'\n readonly year?: 'numeric' | '2-digit'\n readonly month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow'\n readonly day?: 'numeric' | '2-digit'\n readonly hour?: 'numeric' | '2-digit'\n readonly minute?: 'numeric' | '2-digit'\n readonly second?: 'numeric' | '2-digit'\n readonly timeZone?: string\n}\n\n// ============================================================\n// I18N CAPABILITY INTERFACE\n// ============================================================\n\n/**\n * i18n capability interface\n */\nexport interface I18nCapability {\n /** Register a locale definition */\n addLocale(locale: LocaleDefinition): void\n\n /** Load translations for a locale */\n loadTranslations(localeId: LocaleId, catalog: TranslationCatalog): void\n\n /** Resolve the best locale from a list of preferred locales */\n resolveLocale(preferred: readonly LocaleId[]): LocaleId\n\n /** Set the current active locale */\n setLocale(localeId: LocaleId): void\n\n /** Get the current active locale */\n getLocale(): LocaleId\n\n /** Get all registered locale IDs */\n getLocaleIds(): readonly LocaleId[]\n\n /** Translate a key with optional interpolation values */\n translate(key: string, values?: Readonly<Record<string, unknown>>): string\n\n /** Shorthand alias for translate */\n t(key: string, values?: Readonly<Record<string, unknown>>): string\n\n /** Format a number according to the current locale */\n formatNumber(value: number, options?: NumberFormatOptions): string\n\n /** Format a date according to the current locale */\n formatDate(value: Date | number, options?: DateFormatOptions): string\n\n /** Check if a translation key exists */\n hasTranslation(key: string): boolean\n\n /** Clear all locales and translations */\n clear(): void\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pwfabric/runtime
|
|
3
|
+
*
|
|
4
|
+
* Runtime and execution engine for the PWFabric platform.
|
|
5
|
+
*
|
|
6
|
+
* Runtime spine implementation
|
|
7
|
+
* Surface rendering pipeline, layout system, primitives
|
|
8
|
+
* Binding engine, auth, cache, graph resolver
|
|
9
|
+
*
|
|
10
|
+
* @module @pwfabric/runtime
|
|
11
|
+
*/
|
|
12
|
+
export * from '@pwfabric/contracts';
|
|
13
|
+
export { FabricKernel, createFabric } from './kernel.js';
|
|
14
|
+
export { type CapabilityRegistry, InMemoryCapabilityRegistry, createRegistry } from './registry.js';
|
|
15
|
+
export { type ContractValidator, RuntimeContractValidator, createValidator, isValidationSuccess, isValidationFailure, formatValidationErrors, } from './validator.js';
|
|
16
|
+
export { InMemoryEventBus, createEventBus, createFabricEvent } from './bus.js';
|
|
17
|
+
export { type InvocationHandler, RuntimeExecutionContext, ConsoleExecutionLogger, NoOpExecutionLogger, createExecutionContext, generateTraceId, generateSpanId, type CreateExecutionContextOptions, } from './execution-context.js';
|
|
18
|
+
export * from './context.js';
|
|
19
|
+
export * from './executor.js';
|
|
20
|
+
export * from './surface/index.js';
|
|
21
|
+
export * from './binding/index.js';
|
|
22
|
+
export * from './cache/index.js';
|
|
23
|
+
export * from './graph/index.js';
|
|
24
|
+
export * from './state/index.js';
|
|
25
|
+
export * from './forms/index.js';
|
|
26
|
+
export * from './entity/index.js';
|
|
27
|
+
export * from './connectors/index.js';
|
|
28
|
+
export { SurfaceConnectionsContext, useSurfaceConnections } from './render/index.js';
|
|
29
|
+
export type { BlockComponentProps, BlockComponent, BlockComponentRegistry, CapabilityProvider, BlockContext, SurfaceEventType, SurfaceEvent, SurfaceRendererProps, BlockRendererProps, RenderableSurface, CapabilityType, ImageAdapter, ImageAdapterProps, SurfaceDataSourceKind, SurfaceDataSourceDefinition, SurfaceActionType, SurfaceAction, BlockActionProp, DataBoundSurface, DataBoundSurfaceRendererProps, } from './render/index.js';
|
|
30
|
+
export type { EntitlementContextValue, EntitlementProviderProps, GatedBlockPlaceholderProps, } from './render/index.js';
|
|
31
|
+
export { EntitlementProvider, useEntitlements, GatedBlockPlaceholder } from './render/index.js';
|
|
32
|
+
export type { DynamicBlockWrapperProps, DynamicArtifactContextValue, DynamicArtifactProviderProps, } from './render/index.js';
|
|
33
|
+
export { BlockModuleLoader, DynamicBlockWrapper, DynamicArtifactProvider, preloadDynamicBlocks, useDynamicBlocks, useDynamicStarters, } from './render/index.js';
|
|
34
|
+
export { BlockErrorBoundary } from './render/index.js';
|
|
35
|
+
export { dataSourceCacheKey } from './render/index.js';
|
|
36
|
+
export type { SurfaceDataCache } from './render/index.js';
|
|
37
|
+
export { createSurfaceEventBus } from './render/index.js';
|
|
38
|
+
export { SurfaceEventContext } from './render/index.js';
|
|
39
|
+
export { useSurfaceEvents } from './render/index.js';
|
|
40
|
+
export type { BehaviorSurfaceProviderProps } from './render/index.js';
|
|
41
|
+
export { BehaviorSurfaceProvider } from './render/index.js';
|
|
42
|
+
export { SurfaceIdContext } from './render/index.js';
|
|
43
|
+
export { useSurfaceId } from './render/index.js';
|
|
44
|
+
export type { ShellSelectionStore, ShellSelectionOptions, ShellSelectionProviderProps, } from './render/index.js';
|
|
45
|
+
export { ShellSelectionContext, ShellSelectionProvider, createShellSelectionStore, useShellSelection, } from './render/index.js';
|
|
46
|
+
export { DefaultImageAdapter, NextImageAdapter } from './adapters/index.js';
|
|
47
|
+
export { SurfaceRenderer as BlockSurfaceRenderer, BlockRenderer, getBlockIds, findBlockById, updateBlockById, DataBoundSurfaceRenderer, surfaceNeedsDataBinding, blockActionsOf, resolveEndpoint, resolveBlockExpressions, resolveString, surfaceThemeToCSS, } from './render/index.js';
|
|
48
|
+
export * from './capability/index.js';
|
|
49
|
+
export * from './data/index.js';
|
|
50
|
+
export * from './routing/index.js';
|
|
51
|
+
export * from './seo/index.js';
|
|
52
|
+
export * from './analytics/index.js';
|
|
53
|
+
export * from './i18n/index.js';
|
|
54
|
+
export type { WebVitalName, WebVitalRating, WebVitalMetric, WebVitalCallback, WebVitalsReport, RenderMetrics, RenderTimerConfig, PerformanceBudget, BudgetViolation, BudgetCheckResult, ReportDestination, ReporterConfig, PerformanceReport, } from './perf/index.js';
|
|
55
|
+
export { WebVitalsCollector, createWebVitalsCollector, rateMetric, RenderTimer, createRenderTimer, DEFAULT_BUDGET, BudgetChecker, createBudgetChecker, PerformanceReporter, createPerformanceReporter, } from './perf/index.js';
|
|
56
|
+
export type { AnnouncementLevel, LiveRegionConfig } from './a11y/index.js';
|
|
57
|
+
export { LiveRegionAnnouncer, createAnnouncer } from './a11y/index.js';
|
|
58
|
+
export type { FocusTrapConfig } from './a11y/index.js';
|
|
59
|
+
export { FocusTrapManager, FocusRestorer, createSkipLink, createFocusTrap, createFocusRestorer, } from './a11y/index.js';
|
|
60
|
+
export type { ArrowDirection, KeyboardNavConfig } from './a11y/index.js';
|
|
61
|
+
export { KeyboardNavigationManager, RovingTabIndex, createKeyboardNav, createRovingTabIndex, } from './a11y/index.js';
|
|
62
|
+
export type { ContrastResult } from './a11y/index.js';
|
|
63
|
+
export { hexToRgb, relativeLuminance, contrastRatio, checkContrast, meetsWCAG, } from './a11y/index.js';
|
|
64
|
+
export { useInstalledAtoms, type InstalledAtomDescriptor, type InstalledAtomKind, type InstalledAtomsState, type InstalledAtomsFetcher, type AtomManifestPayload, type AtomBlockDefinitionMetadata, } from './hooks/useInstalledAtoms.js';
|