mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-12-07 19:03:54 +00:00
420 lines
8.8 KiB
ReStructuredText
420 lines
8.8 KiB
ReStructuredText
============================================
|
|
Directives for creating new translation keys
|
|
============================================
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
<scope>.<feature>.<sub-feature>.<key-type>
|
|
|
|
Where:
|
|
|
|
* ``<>`` 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 ( eg. 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:
|
|
|
|
.. code-block:: text
|
|
|
|
activity.<feature>.<element>
|
|
|
|
Example:
|
|
|
|
.. code-block:: text
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
activity.controller.success_created
|
|
activity.controller.error_invalid_create
|
|
activity.controller.success_updated
|
|
activity.controller.error_invalid_update
|
|
|
|
|
|
Roles
|
|
-----
|
|
|
|
Access control keys should be:
|
|
|
|
.. code-block:: text
|
|
|
|
activity.role.create
|
|
activity.role.update
|
|
activity.role.see
|
|
activity.role.see_details
|
|
activity.role.delete
|
|
activity.role.stats
|
|
activity.role.list
|
|
|
|
|
|
Admin
|
|
-----
|
|
|
|
.. code-block:: text
|
|
|
|
activity.admin.configuration
|
|
activity.admin.types
|
|
activity.admin.reasons
|
|
activity.admin.reason_category
|
|
activity.admin.presence
|
|
|
|
|
|
CRUD
|
|
----
|
|
|
|
.. code-block:: text
|
|
|
|
activity.crud.type.title_new
|
|
activity.crud.type.title_edit
|
|
activity.crud.presence.title_new
|
|
|
|
|
|
Activity Reason
|
|
---------------
|
|
|
|
.. code-block:: text
|
|
|
|
activity.reason.list
|
|
activity.reason.create
|
|
activity.reason.active
|
|
activity.reason.category
|
|
activity.reason.entity_title
|
|
|
|
|
|
Exports
|
|
-------
|
|
|
|
Group them logically:
|
|
|
|
.. code-block:: text
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
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
|
|
-----------
|
|
|
|
.. code-block:: text
|
|
|
|
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``:
|
|
|
|
.. code-block:: text
|
|
|
|
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:
|
|
|
|
.. code-block:: text
|
|
|
|
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.
|
|
3. 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
|
|
|
|
6. 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.
|