Translation Key Directives¶
These directives are meant to ensure better consistency across bundles, avoid duplication, and make keys more predictable.
General Principles¶
-
Use lowercase snake_case for all keys
-
Use dot-separated namespaces The dot is used to reflect:
- bundle
- feature
- sub-feature
- key type
-
Do not use spaces in keys
-
Avoid duplicating the same text in multiple places When a translation is needed, try a search for the translation value first and see if it exists elsewhere
-
If a key is used across multiple bundles, it must live in ChillMainBundle.
-
If a key is used across multiple bundles and is a generic term, it must be placed in the
commonnamespace.
Key Structure¶
We use the following structure:
Where:
<scope>identifies the bundle or shared context<feature>identifies the part of the module using the translation<element>describes the text purpose<subelement>for a multi-level element (e.g., activity.export.person.count.description)
Examples of scopes¶
activity— ChillActivityBundleperson— ChillPersonBundlecommon— neutral shared translation values
Naming Scopes¶
1. Bundle-specific keys¶
For most things inside a bundle:
Example:
activity.form.save
activity.list.title
activity.entity.type
activity.menu.activities
activity.controller.success_created
2. Shared UI elements (buttons, labels, generic text)¶
These belong in the common namespace in ChillMainBundle:
Translation Workflow¶
Use the following workflow when deciding where a key belongs:
-
Is this text used in more than one bundle? → Place in
mainorcommon -
Is this text generic UI (button, label, pagination, yes/no)? → Place in
common -
Is this text specific to one bundle and one feature? → Place in
<bundle>.feature.<element> -
Is this text related to an entity or value object? → Place in
<bundle>.entity.<entityname>.<field> -
Is this text used in forms? →
<bundle>.form.<field>or<bundle>.form.<action> -
Is this text related to exports? →
<bundle>.export.<feature>.<column> -
Is it related to filtering, searching or parameters? →
<bundle>.filter.<name>or →<bundle>.filter.<feature>.<field>for nested filters
Examples Based on Translations Within ChillActivityBundle¶
Below are concrete examples from ChillActivityBundle, refactored according to the guidelines.
General activity keys¶
Instead of scattered keys like:
We use:
activity.general.show
activity.general.edit
activity.general.title
activity.general.duration
activity.general.travel_time
activity.general.attendee
activity.general.remark
activity.general.no_comments
Forms¶
Instead of keys like:
Use:
activity.form.title_create
activity.form.save
activity.form.reset
activity.form.choose_type
activity.form.choose_duration
Long lists (like durations) should be grouped:
activity.form.duration.5min
activity.form.duration.10min
activity.form.duration.15min
activity.form.duration.1h
activity.form.duration.1h30
activity.form.duration.2h
...
Entities¶
Entity fields should follow:
activity.entity.activity.date
activity.entity.activity.comment
activity.entity.activity.deleted
activity.entity.location.name
activity.entity.location.type
Controller messages¶
Instead of strings as keys:
Use:
activity.controller.success_created
activity.controller.error_invalid_create
activity.controller.success_updated
activity.controller.error_invalid_update
Roles¶
Access control keys should be:
activity.role.create
activity.role.update
activity.role.see
activity.role.see_details
activity.role.delete
activity.role.stats
activity.role.list
Admin¶
activity.admin.configuration
activity.admin.types
activity.admin.reasons
activity.admin.reason_category
activity.admin.presence
CRUD¶
Activity Reason¶
activity.reason.list
activity.reason.create
activity.reason.active
activity.reason.category
activity.reason.entity_title
Exports¶
Group them logically:
activity.export.person.count.title
activity.export.person.count.description
activity.export.person.count.header
activity.export.period.sum_duration.title
activity.export.period.sum_duration.description
activity.export.period.sum_duration.header
Filters¶
Use hierarchical filters:
activity.filter.by_reason
activity.filter.by_type
activity.filter.by_date
activity.filter.by_location
activity.filter.by_sent_received
activity.filter.by_user
Aggregators¶
activity.aggregator.reason.by_category
activity.aggregator.reason.level
activity.aggregator.user.by_scope
activity.aggregator.user.by_job
Global/Shared Keys¶
Keys like the following must not be redeclared in each bundle:
- First name
- Last name
- Username
- ID
- Type
- Duration
- Comment
- Date
- Location
- Present / Not present
- Add / Edit / Delete / Save / Update
These belong in common or main:
common.firstname
common.lastname
common.username
common.id
common.type
common.comment
common.date
common.location
common.present
common.absent
common.add
common.edit
common.delete
common.save
common.update
Naming Directives Summary¶
- snake_case
- namespaced with dots
- bundle prefix for bundle-specific concepts
- common or main for shared concepts
- avoid free-floating keys (without namespace)
- reuse common keys wherever possible
Migration Strategy (Optional)¶
To apply this structure progressively:
- New keys must follow these guidelines.
- Existing keys may remain as-is until refactored.
- When refactoring:
- Move cross-bundle keys to ChillMainBundle and possible
commonnamespace. - Replace duplicated keys with shared ones.
- Move cross-bundle keys to ChillMainBundle and possible
Avoiding Duplicate Translations¶
1. Use Shared Namespaces¶
Two namespaces must be used for shared translations:
common.*— generic UI concepts (save, delete, date, name, etc.)
If a translation may be reused in multiple bundles, it must be placed in the common namespace or in ChillMainBundle.
2. Bundle-Specific Keys¶
Keys belonging only to one bundle or one feature are namespaced inside that bundle:
3. Search Before Creating¶
Before adding a new translation key, developers must:
- For common translations like "enregistrer/opslaan" look in the
commonnamespace. - Search in Loco or translations for existing values.
If a suitable key exists, reuse it.
4. Only Create a New Key When Necessary¶
Create a new key only when the text is:
- specific to the bundle
- specific to the feature
- not reusable elsewhere
5. Progressive Cleanup¶
Old duplicates may remain temporarily. When updating code in an area, clean duplicate values by moving them into common or main.
General Workflow¶
- Reuse shared keys within
commonnamespace. - Search before creating new keys.
- Namespace bundle-specific keys under their bundle.
- Refactor progressively when touching old code.