Language & Text
Code identifiers and comments must be in English.
User-facing non-English copy must live in translation files, not directly in PHP/Blade source. Report this rule only when the text is clearly rendered to the user, such as labels, buttons, headings, notifications, dialogs, validation messages, emails, or Blade output.
Do not use this rule to require translation wrappers for already-English text. Translation helper usage is a separate consistency concern.
When adding new localized user-facing copy, use translation helpers and update every project locale JSON file that exists for the UI, commonly lang/en.json and lang/es.json. Do not add the helper in PHP/Blade without verifying or adding the matching entries in those locale files.
When fixing user-facing non-English copy, prefer a literal translation key such as __('Format saved'), __('Edit'), or __('Remote'). Do not invent abstract, numbered, variable-like, or namespaced translation keys such as __('validator.text1'), __('message_1'), or __('module.section.label') unless the project already has that convention or the current change explicitly adds the required translation entries as part of a deliberate i18n change.
When replacing user-facing non-English copy with a translation helper, update the appropriate JSON translation file in the same change unless the key already exists. Preserve the original visible text exactly as the translation value, including accents, punctuation, capitalization, and spacing. Do not leave the source code changed to __('English key') without adding or verifying the matching JSON entry.
Long-form copy is the exception to the literal key. A legal page, a policy, or an email body is a block of prose with no short literal that could name it, and it is rewritten over time without the code changing. Give it a named key in a locale file of its own, such as lang/es/legal.php, instead of a JSON entry. The block keeps the inline markup that belongs to it — a <strong> around a defined term, a bold label opening a list item — and the page renders it unescaped, passing any value it interpolates already escaped.
A date that tells the reader when a document was last revised belongs to the copy, beside the text it dates. Do not derive it from the filesystem: filemtime() in a Blade file reads the compiled view, so clearing the view cache silently restamps a legal document as revised today, and even against the source it would move whenever the markup is touched for unrelated reasons.
Move long-form copy in the language it was written in, then translate it in a separate change. Relocating and translating at once leaves a diff where nothing can be checked: a clause that was dropped in the move looks exactly like a clause that was reworded in the translation. Moving first keeps the text verifiable against the page it came from.
Do not apply the user-facing copy rule to tests that assert expected rendered text. Test assertions may contain or reference the exact non-English copy they verify because they are not themselves rendered to users.
Do not apply the user-facing copy rule to internal persisted values, enum/database values, array keys, route names, event names, config keys, or comparison tokens that are not directly rendered to users. For example, a status-to-color map keyed by stored Spanish status values is internal logic, not visible copy.