Skip to content

feat(config): add configuration tree and migrate to AbstractBundle#85

Merged
paullla merged 1 commit into
Locastic:masterfrom
paullla:feat/bundle-configuration
Jul 8, 2026
Merged

feat(config): add configuration tree and migrate to AbstractBundle#85
paullla merged 1 commit into
Locastic:masterfrom
paullla:feat/bundle-configuration

Conversation

@paullla

@paullla paullla commented Jul 8, 2026

Copy link
Copy Markdown
Member
Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? yes
Issues relates to #55 (groundwork for opt-in translation auto-creation)
License MIT

Problem

The bundle still uses the legacy layout (src/Resources/config/services.yml, a hand-written Extension class) and has no configuration tree at all: locale restriction is only inherited from framework.enabled_locales, the fallback locale stamped on translatables is hard-wired to kernel.default_locale, and the locale resolution order (query parameter, then Accept-Language) cannot be changed or partially disabled. Upcoming behavioral options (such as opt-in translation auto-creation) need a place to live.

Changes

  • The bundle class extends AbstractBundle: configure() defines the api_platform_translation configuration tree, loadExtension() sets the locastic_api_platform_translation.* parameters and imports the services file. This is the documented recommended setup for new bundles. The extension alias is pinned explicitly and guarded by a test, since the config key is public API.
  • New configuration options, all defaults preserving current behavior: enabled_locales (empty inherits framework.enabled_locales), fallback_locale (null inherits kernel.default_locale), locale_resolution (ordered query_param / accept_language sources, each removable, duplicates rejected).
  • Modern layout: services moved to config/services.php (PHP DSL) at the bundle root and src/Resources/ removed; AbstractBundle's default getPath() already resolves the modern layout, so no override is needed. Service IDs, tags and the translation.groups filter id are unchanged; a FQCN alias for Translator was added so it can be autowired by type.
  • ApiPlatformTranslationExtension is deprecated for removal in 3.0 but keeps working standalone (it now loads the PHP services file and sets the parameters itself), so the minor stays BC-clean; documented in UPGRADE-2.1.md.
  • Translator::loadCurrentLocale() iterates the configured sources. Surprising but deliberate: a request without an Accept-Language header now falls through to the next source instead of resolving to the head of the negotiation list, which is what makes non-default orders like [accept_language, query_param] meaningful. With the default order the observable behavior is identical (covered by the existing test rows). An explicit but not-enabled locale still pins the default instead of falling through.
  • Dependency hygiene: symfony/config, symfony/http-kernel and symfony/deprecation-contracts are now direct requirements (all used directly), symfony/yaml is dropped since no YAML is loaded at runtime anymore.
  • Test infrastructure: TestKernel accepts per-test bundle and framework config (hashed into the cache dir so container caches never collide) and points its app-level config dir into the cache dir, because Symfony 8.1 debug kernels dump a config/reference.php into the project config dir, which is now a real bundle directory.
  • README gained a Configuration section; CHANGELOG updated.

Verification

  • bin/phpunit: 82 tests, 166 assertions, green (9 pre-existing PHPUnit deprecations from legacy non-static data providers, count unchanged). New coverage: functional kernel tests for defaults, framework inheritance, bundle overrides, resolution-order wiring and rejection of unknown or duplicate sources; unit tests for every resolution order variant; a legacy-extension standalone test; an extension-alias stability test.
  • bin/phpstan analyse (level 6, now also covering config/): no errors.
  • bin/php-cs-fixer fix --dry-run (now also covering config/): clean.
  • composer validate --strict: valid.
  • Setup verified against the Symfony 7.4 bundle docs (configuration, extension, best practices pages) and the 6.4.0 AbstractBundle source for the getPath() behavior on the oldest supported Symfony.
  • Verified in the demo apps over real HTTP. translation-demo (Symfony 7.4, API Platform 4.3, sqlite): composer update of the bundle resolves the new constraints and the container compiles; with no bundle config, all six baseline resolution cases behave exactly like 2.0 (query param, header negotiation with q-values, query beating header, non-enabled locale pinning the default); with enabled_locales: [en, de, hr], fallback_locale: de, locale_resolution: [accept_language] the query parameter is ignored, the header wins, and a request for hr (enabled, no translation) falls back to the German translation, proving the fallback_locale wiring; locale_resolution: [cookie] and duplicate sources fail the container build with the expected messages; PATCH adding a translation still works (denormalizer wired through the new services file). translation-demo-ap34 (API Platform 3.4): container compiles, POST with nested translations, ?locale=, Accept-Language and the groups[]=translations filter (the ChildDefinition of api_platform.serializer.group_filter) all behave correctly.

Migrate the bundle to the modern layout: the bundle class extends
AbstractBundle, defines the api_platform_translation configuration tree
in configure() and imports root-level config/services.php (PHP DSL)
from loadExtension(). The legacy ApiPlatformTranslationExtension keeps
working standalone but is deprecated for removal in 3.0.

New configuration, all defaults preserving current behavior:

- enabled_locales: accepted request locales, empty inherits
  framework.enabled_locales
- fallback_locale: locale stamped as fallback on translatables, null
  inherits kernel.default_locale
- locale_resolution: ordered locale sources (query_param,
  accept_language), each can be removed or reordered

Translator resolves the locale by iterating the configured sources. A
request without an Accept-Language header now falls through to the next
source instead of resolving to the head of the negotiation list, which
is what makes non-default orders meaningful; with the default order the
observable behavior is unchanged.

Dependency hygiene: symfony/config, symfony/http-kernel and
symfony/deprecation-contracts are now direct requirements, symfony/yaml
is dropped (no YAML is loaded at runtime anymore).
@paullla paullla merged commit 3b06395 into Locastic:master Jul 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant