Files
chill-bundles/docs/source/development/translation_directives.md

7.5 KiB

Translation Key Directives

These directives are meant to ensure better consistency across bundles, avoid duplication, and make keys more predictable.

General Principles

  1. Use lowercase snake_case for all keys

  2. Use dot-separated namespaces The dot is used to reflect:

    • bundle
    • feature
    • sub-feature
    • key type
  3. Do not use spaces in keys

  4. 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

  5. If a key is used across multiple bundles, it must live in ChillMainBundle.

  6. If a key is used across multiple bundles and is a generic term, it must be placed in the common namespace.

Key Structure

We use the following structure:

<scope>.<feature>.<sub-feature>.<key-type>

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 — ChillActivityBundle
  • person — ChillPersonBundle
  • common — neutral shared translation values

Naming Scopes

1. Bundle-specific keys

For most things inside a bundle:

activity.<feature>.<element>

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:

common.save
common.delete
common.edit
common.filter
common.duration_time

Translation Workflow

Use the following workflow when deciding where a key belongs:

  1. Is this text used in more than one bundle? → Place in main or common

  2. Is this text generic UI (button, label, pagination, yes/no)? → Place in common

  3. Is this text specific to one bundle and one feature? → Place in <bundle>.feature.<element>

  4. Is this text related to an entity or value object? → Place in <bundle>.entity.<entityname>.<field>

  5. Is this text used in forms?<bundle>.form.<field> or <bundle>.form.<action>

  6. Is this text related to exports?<bundle>.export.<feature>.<column>

  7. 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:

Show the activity
Edit the activity
Activity
Duration time
...

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:

Activity creation
Save activity
Reset form
Choose a type

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:

'Success : activity created!'
'The form is not valid. The activity has not been created !'

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.crud.type.title_new
activity.crud.type.title_edit
activity.crud.presence.title_new

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:

  1. New keys must follow these guidelines.
  2. Existing keys may remain as-is until refactored.
  3. When refactoring:
    • Move cross-bundle keys to ChillMainBundle and possible common namespace.
    • Replace duplicated keys with shared ones.

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:

activity.<feature>.<element>
person.<feature>.<element>

3. Search Before Creating

Before adding a new translation key, developers must:

  1. For common translations like "enregistrer/opslaan" look in the common namespace.
  2. 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 common namespace.
  • Search before creating new keys.
  • Namespace bundle-specific keys under their bundle.
  • Refactor progressively when touching old code.