repowarden 0.0.0-stage → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -2
  3. package/bin/code-mort +33 -0
  4. package/bin/commit +412 -0
  5. package/bin/npm-publication +151 -0
  6. package/bin/proteger +283 -0
  7. package/bin/repogarde +20 -0
  8. package/bin/repowarden +63 -0
  9. package/bin/statut +100 -0
  10. package/bin/ticket +129 -0
  11. package/bin/tickets +135 -0
  12. package/hooks/commit-msg +64 -0
  13. package/hooks/lang/actions.sh +9 -0
  14. package/hooks/lang/ansible.sh +18 -0
  15. package/hooks/lang/cpp.sh +22 -0
  16. package/hooks/lang/dart.sh +20 -0
  17. package/hooks/lang/deno.sh +15 -0
  18. package/hooks/lang/docker.sh +26 -0
  19. package/hooks/lang/dotnet.sh +15 -0
  20. package/hooks/lang/elixir.sh +13 -0
  21. package/hooks/lang/go.sh +22 -0
  22. package/hooks/lang/gradle.sh +28 -0
  23. package/hooks/lang/helm.sh +28 -0
  24. package/hooks/lang/kubernetes.sh +29 -0
  25. package/hooks/lang/maven.sh +27 -0
  26. package/hooks/lang/node.sh +75 -0
  27. package/hooks/lang/packer.sh +16 -0
  28. package/hooks/lang/php.sh +20 -0
  29. package/hooks/lang/python.sh +79 -0
  30. package/hooks/lang/ruby.sh +24 -0
  31. package/hooks/lang/rust.sh +17 -0
  32. package/hooks/lang/shell.sh +9 -0
  33. package/hooks/lang/swift.sh +20 -0
  34. package/hooks/lang/tasks.sh +16 -0
  35. package/hooks/lang/terraform.sh +29 -0
  36. package/hooks/lib/common.sh +393 -0
  37. package/hooks/lib/compat.sh +24 -0
  38. package/hooks/lib/deadcode.sh +153 -0
  39. package/hooks/lib/forge.sh +39 -0
  40. package/hooks/lib/i18n/en.sh +393 -0
  41. package/hooks/lib/i18n/fr.sh +393 -0
  42. package/hooks/lib/i18n.sh +81 -0
  43. package/hooks/lib/lang.sh +338 -0
  44. package/hooks/lib/safety.sh +108 -0
  45. package/hooks/lib/tickets.sh +184 -0
  46. package/hooks/lib/ui.sh +40 -0
  47. package/hooks/post-checkout +26 -0
  48. package/hooks/post-merge +47 -0
  49. package/hooks/pre-commit +58 -0
  50. package/hooks/pre-push +91 -0
  51. package/hooks/prepare-commit-msg +48 -0
  52. package/install.sh +245 -0
  53. package/package.json +31 -4
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env bash
2
+ # Catalogue français (langue de référence : toute clé existe ici).
3
+ # Formats printf : %s pour les arguments, %% pour un « % » littéral.
4
+ _msg_fr() {
5
+ case "$1" in
6
+ # Assistant de commit (git cc)
7
+ cc.title) _M="commit conventionnel" ;;
8
+ cc.help) _M="Entrée = valeur proposée entre [crochets] · Ctrl+C pour abandonner" ;;
9
+ cc.nothing_staged) _M="Rien n'est stagé : ajoute d'abord tes modifications (git add …)." ;;
10
+ cc.step1) _M="Type de changement" ;;
11
+ cc.type.feat) _M="nouvelle fonctionnalité" ;;
12
+ cc.type.fix) _M="correction de bug" ;;
13
+ cc.type.docs) _M="documentation uniquement" ;;
14
+ cc.type.style) _M="formatage, sans changement de logique" ;;
15
+ cc.type.refactor) _M="restructuration sans changement de comportement" ;;
16
+ cc.type.perf) _M="amélioration de performance" ;;
17
+ cc.type.test) _M="ajout ou modification de tests" ;;
18
+ cc.type.build) _M="build, dépendances" ;;
19
+ cc.type.ci) _M="intégration continue" ;;
20
+ cc.type.chore) _M="maintenance diverse" ;;
21
+ cc.type.revert) _M="annulation d'un commit" ;;
22
+ cc.type_prompt) _M="Numéro ou nom [%s] : " ;;
23
+ cc.type_prompt_required) _M="Numéro ou nom : " ;;
24
+ cc.type_required) _M="Type obligatoire : la branche ne commence pas par un type (ex. feat/…, fix/…)." ;;
25
+ cc.option_value) _M="L'option %s attend une valeur." ;;
26
+ cc.batch_message_required) _M="Sans question, la description est obligatoire : git cc -m \"<description>\" [--type T] [--scope S] [--body …] [--refs …] [--breaking …]." ;;
27
+ cc.type_invalid) _M="« %s » : choisis un numéro (1 à %s) ou un nom de la liste." ;;
28
+ cc.step2) _M="Scope (optionnel)" ;;
29
+ cc.scope_hint) _M="Partie du projet concernée, ex. api, auth, chart ; Entrée pour aucun." ;;
30
+ cc.scope_prompt_default) _M="Scope (« - » pour aucun) [%s] : " ;;
31
+ cc.scope_prompt) _M="Scope : " ;;
32
+ cc.scope_fixed) _M="Scope : minuscules, chiffres, . _ - uniquement. Proposé : %s (Entrée pour l'accepter)." ;;
33
+ cc.scope_invalid) _M="Scope : minuscules, chiffres, . _ - uniquement." ;;
34
+ cc.step3) _M="Changement incompatible ?" ;;
35
+ cc.first_commit) _M="Premier commit du dépôt : rien d'existant à casser, question sautée." ;;
36
+ cc.breaking_hint1) _M="Oui seulement si l'existant cesse de fonctionner pour ses utilisateurs" ;;
37
+ cc.breaking_hint2) _M="(API, configuration, format, commande supprimés ou modifiés). Version majeure." ;;
38
+ cc.breaking_prompt) _M="Incompatible ? [o/N] : " ;;
39
+ cc.breaking_note_hint) _M="Ce qui casse et comment migrer (affiché dans les notes de version)." ;;
40
+ cc.breaking_note_prompt) _M="Ce qui change : " ;;
41
+ cc.breaking_note_invalid) _M="Décris le changement (10 caractères au moins) ; sinon, réponds non à la question précédente." ;;
42
+ cc.step4) _M="Description" ;;
43
+ cc.desc_hint) _M="À l'impératif, sans majuscule initiale ni point final ; %s caractères au plus." ;;
44
+ cc.desc_required) _M="La description est obligatoire." ;;
45
+ cc.header_too_long) _M="En-tête trop long : %s caractères (72 max). Raccourcis la description." ;;
46
+ cc.step5) _M="Détails (optionnels)" ;;
47
+ cc.body_hint) _M="Corps : explique le pourquoi, sur plusieurs lignes ; Entrée sur une ligne vide pour terminer." ;;
48
+ cc.refs_prompt_ticket) _M="Références (« - » pour aucune) [%s] : " ;;
49
+ cc.refs_prompt) _M="Références (ex. %s ; Entrée pour aucune) : " ;;
50
+ tk.cli.no_ticket) _M="Branche %s sans ticket : ce travail ne sera relié à aucun ticket (non bloquant)." ;;
51
+ tk.cli.propose) _M="Le partir d'un ticket : repowarden ticket nouveau \"<titre>\" (ou réponds oui à git cc)." ;;
52
+ tk.cli.silence) _M="Projet sans suivi de tickets ? git config repowarden.skip tickets pour ne plus le voir." ;;
53
+ tk.cli.fetch) _M="Branche du ticket #%s…" ;;
54
+ tk.cli.switched) _M="Sur la branche %s (ticket #%s)." ;;
55
+ tk.cli.created_branch) _M="Branche %s créée depuis %s, liée au ticket #%s." ;;
56
+ tk.cli.not_validated) _M="Ticket #%s pas encore validé : sa branche sera créée automatiquement à la validation (étiquette « %s »)." ;;
57
+ tk.cli.closed) _M="Ticket #%s fermé : pas de branche à créer." ;;
58
+ tk.cli.issue_body) _M="Ticket créé avec repowarden ticket nouveau. Besoin et critères d'acceptation à compléter." ;;
59
+ tk.cli.issue_created) _M="Ticket #%s créé, à valider. Sa branche sera créée à la validation ; ensuite : repowarden ticket %s" ;;
60
+ tk.cli.issue_created_branch) _M="Ticket #%s créé." ;;
61
+ tk.cli.title_required) _M="Titre du ticket obligatoire : repowarden ticket nouveau \"<titre>\"" ;;
62
+ tk.cli.current) _M="Branche %s : ticket #%s." ;;
63
+ cc.ticket_offer) _M="Aucun ticket pour cette branche. En créer un maintenant (titre : la description) ? [o/N] : " ;;
64
+ cc.ticket_created) _M="Ticket #%s créé et cité dans le message." ;;
65
+ cc.preview) _M="Message" ;;
66
+ cc.confirm) _M="Commiter ? [O/n] : " ;;
67
+ cc.aborted) _M="Abandonné : rien n'a été commité." ;;
68
+ cc.yn_invalid) _M="Réponds o (oui) ou n (non)." ;;
69
+ cc.topic_default) _M="modification" ;;
70
+ cc.protected_hint) _M="Tu es sur « %s », protégée : un commit y serait refusé. Il ira sur une nouvelle branche (tes modifications suivent)." ;;
71
+ cc.new_branch_prompt) _M="Nouvelle branche (« n » pour abandonner) [%s] : " ;;
72
+ cc.branch_exists) _M="La branche « %s » existe déjà : choisis un autre nom." ;;
73
+ cc.branch_fixed) _M="« %s » : nom attendu <type>/<sujet> en minuscules. Proposé : %s (Entrée pour l'accepter)." ;;
74
+ cc.branch_created) _M="Branche %s créée, tes modifications y sont." ;;
75
+ cc.strict_abort) _M="Abandon (--strict) : réponse refusée, rien n'a été commité." ;;
76
+ cc.kept) _M="Commit refusé : ton message est conservé. Après correction : git commit -F %s" ;;
77
+ cc.created) _M="Commit créé." ;;
78
+ cc.next_branch) _M="Prochains changements : sur une branche, ex. %s, puis une %s." ;;
79
+ cc.topic) _M="sujet" ;;
80
+ cc.next_push) _M="Envoyer : %s" ;;
81
+ hook.version_old) _M="Ce projet demande repowarden %s ou plus récent ; installé sur ce poste : %s. Mettre à jour : %s" ;;
82
+ # Installation
83
+ install.unknown_arg) _M="Argument inconnu : %s" ;;
84
+ install.not_repo) _M="Pas dans un dépôt git (utilise --global pour tous les dépôts)." ;;
85
+ install.alias_taken) _M="Alias git cc déjà utilisé ('%s') : assistant de commit non installé." ;;
86
+ install.alias_ok) _M="Assistant de commit : git cc" ;;
87
+ install.alias_removed) _M="Alias git cc retiré (%s)." ;;
88
+ install.npm_remove) _M="Le paquet reste installé ; pour le retirer aussi : %s" ;;
89
+ install.nothing) _M="Aucun core.hooksPath (%s) : rien à retirer." ;;
90
+ install.removed) _M="Hooks repowarden retirés (%s)." ;;
91
+ install.foreign) _M="core.hooksPath (%s) = '%s' n'est pas repowarden : laissé intact." ;;
92
+ install.cache_removed) _M="Cache supprimé : %s" ;;
93
+ install.scan) _M="Dépôts de %s encore branchés sur repowarden :" ;;
94
+ install.none) _M="aucun" ;;
95
+ install.last_step) _M="Dernière étape, à lancer toi-même si tu n'utilises plus repowarden :" ;;
96
+ install.purge_needs_uninstall) _M="--purge et --scan s'utilisent avec --uninstall." ;;
97
+ install.replaced) _M="core.hooksPath (%s) valait '%s', remplacé." ;;
98
+ install.enabled) _M="Hooks activés (%s) → %s" ;;
99
+ install.local_wins) _M="Un core.hooksPath local (ex. husky) reste prioritaire dans le dépôt concerné." ;;
100
+ install.lang_set) _M="Langue des messages : français (changer : %s --global --lang en)" ;;
101
+ install.lang_invalid) _M="--lang : fr ou en." ;;
102
+ install.keys_migrated) _M="Réglages repogarde.* (%s) renommés en repowarden.*" ;;
103
+ # Hooks et plugins de langage
104
+ hook.pre_commit.1) _M="Commit refusé : nom de branche non conforme." ;;
105
+ hook.pre_commit.2) _M="Commit direct sur '%s' interdit. Crée une branche : git switch -c feat/ma-feature" ;;
106
+ hook.pre_commit.3) _M=" (désactiver pour ce dépôt : git config repowarden.skip protect-branch)" ;;
107
+ hook.pre_commit.4) _M="Détection de secrets (gitleaks)" ;;
108
+ hook.pre_commit.5) _M="Secret détecté : commit refusé." ;;
109
+ hook.pre_commit.6) _M="gitleaks absent, détection de secrets ignorée (installation : %s)." ;;
110
+ hook.pre_commit.7) _M="Pre-commit OK" ;;
111
+ hook.commit_msg.1) _M="Première ligne trop longue (%s > 72 caractères)." ;;
112
+ hook.pre_push.1) _M="Push refusé : nom de branche non conforme." ;;
113
+ hook.pre_push.2) _M="Push direct refusé sur '%s' : elle n'évolue que par Pull Request." ;;
114
+ hook.pre_push.3) _M=" Crée une branche : git switch -c feat/ma-feature, puis ouvre une PR." ;;
115
+ hook.pre_push.4) _M=" (désactiver pour ce dépôt : git config repowarden.skip protect-branch)" ;;
116
+ hook.pre_push.5) _M="%s : aucun fichier modifié, rien à tester." ;;
117
+ hook.pre_push.6) _M="Rien à tester (suppression de branche uniquement)." ;;
118
+ hook.pre_push.7) _M="Pre-push OK" ;;
119
+ hook.branch_hint.1) _M="Nom de branche « %s » non conforme : commits et push seront refusés. Renommer :" ;;
120
+ hook.branch_hint.2) _M=" Format <type>/<sujet> (ex. feat/inscription) ; aide complète au prochain commit." ;;
121
+ hook.post_merge.1) _M="🧹 Branche locale supprimée (mergée et supprimée sur le serveur) : %s" ;;
122
+ hook.post_merge.2) _M="🧹 Branche locale supprimée (mergée en squash et supprimée sur le serveur) : %s" ;;
123
+ hook.post_merge.3) _M="%s : supprimée sur le serveur mais des modifications manquent ici, conservée." ;;
124
+ hook.post_merge.4) _M=" Pour la supprimer quand même : git branch -D %s" ;;
125
+ hook.common.3) _M="%s désactivé (git config repowarden.skip)." ;;
126
+ hook.common.lefthook_ignored) _M="%s : hook installé par lefthook, ignoré (lefthook n'est plus pris en charge depuis repowarden 4). Déplacer ses commandes dans .repowarden/%s, puis : lefthook uninstall" ;;
127
+ compat.old_names) _M="Ancien nom repogarde encore utilisé (%s) : renommer en repowarden (REPOWARDEN_*, repowarden.*, .repowarden.conf, .repowarden/). Les anciens noms seront ignorés en v5." ;;
128
+ compat.old_command) _M="La commande repogarde s'appelle désormais repowarden (npm install -g repowarden). L'ancien nom sera retiré en v5." ;;
129
+ hook.common.5) _M="Hook local du projet : %s" ;;
130
+ hook.lang.1) _M="Formatage personnalisé : %s" ;;
131
+ hook.lang.2) _M="%s : %s fichier(s)" ;;
132
+ hook.lang.3) _M="Échec du formatage %s : erreur de syntaxe, ou outil / dépendance indisponible (voir le log ci-dessus)." ;;
133
+ hook.lang.4) _M="Tests personnalisés : %s" ;;
134
+ hook.lang.5) _M="Tests %s" ;;
135
+ hook.lang.6) _M="Échec des tests %s : push refusé." ;;
136
+ hook.lang.7) _M="Aucun projet testable touché par ce push." ;;
137
+ hook.safety.1) _M="Modifications non stagées mises de côté le temps du formatage." ;;
138
+ hook.safety.2) _M="Formatage et modifications non stagées se chevauchent :" ;;
139
+ hook.safety.3) _M="le commit est formaté, ton dossier de travail est resté tel quel (non formaté)." ;;
140
+ hook.safety.4) _M="Modifications locales mises de côté : tests sur le commit poussé." ;;
141
+ hook.safety.5) _M="Tests de %s dans un worktree temporaire." ;;
142
+ hook.safety.6) _M="Impossible de restaurer automatiquement tes modifications." ;;
143
+ hook.safety.7) _M="Elles sont dans le stash : git stash list / git stash pop --index" ;;
144
+ lang.actions.1) _M="GitHub Actions : actionlint" ;;
145
+ lang.ansible.1) _M="Ansible : ansible-lint --fix=none (reformatage YAML)" ;;
146
+ lang.ansible.2) _M="Ansible : ansible-lint" ;;
147
+ lang.cpp.1) _M="C/C++ : clang-format" ;;
148
+ lang.cpp.2) _M="C/C++ : cmake --build + ctest" ;;
149
+ lang.cpp.3) _M="C/C++ : meson test" ;;
150
+ lang.cpp.4) _M="C/C++ : pas de dossier de build configuré (build/ ou builddir/), tests ignorés." ;;
151
+ lang.dart.1) _M="Dart : dart format" ;;
152
+ lang.dart.2) _M="Flutter : flutter test" ;;
153
+ lang.dart.3) _M="Dart : dart test" ;;
154
+ lang.deno.1) _M="Deno : deno fmt" ;;
155
+ lang.deno.2) _M="Deno : deno test" ;;
156
+ lang.docker.1) _M="Docker : hadolint %s" ;;
157
+ lang.docker.2) _M="Docker : hadolint absent, %s non vérifié." ;;
158
+ lang.docker.3) _M="Docker : compose config (%s)" ;;
159
+ lang.docker.4) _M="Docker : docker compose absent, %s non vérifié." ;;
160
+ lang.dotnet.1) _M=".NET : dotnet format" ;;
161
+ lang.dotnet.2) _M=".NET : dotnet test" ;;
162
+ lang.elixir.1) _M="Elixir : mix format" ;;
163
+ lang.elixir.2) _M="Elixir : mix test" ;;
164
+ lang.go.1) _M="Go : gofmt" ;;
165
+ lang.go.2) _M="Go : go test ./..." ;;
166
+ lang.gradle.1) _M="Gradle : spotlessApply (fichiers stagés)" ;;
167
+ lang.helm.1) _M="Helm : dependency build" ;;
168
+ lang.helm.2) _M="Helm : lint + template" ;;
169
+ lang.helm.3) _M="Helm : kubeconform (schémas Kubernetes)" ;;
170
+ lang.helm.4) _M="Helm : unittest" ;;
171
+ lang.kubernetes.1) _M="Kubernetes : kubectl kustomize" ;;
172
+ lang.kubernetes.2) _M="Kubernetes : kustomize build" ;;
173
+ lang.kubernetes.3) _M="Kubernetes : ni kubectl ni kustomize, vérification ignorée." ;;
174
+ lang.kubernetes.4) _M="Kubernetes : kubeconform (schémas)" ;;
175
+ lang.maven.1) _M="Maven : spotless:apply" ;;
176
+ lang.node.1) _M="Node : prettier" ;;
177
+ lang.node.2) _M="Node : pas de script \"test\" dans package.json." ;;
178
+ lang.node.3) _M="Node : %s test" ;;
179
+ lang.packer.1) _M="Packer : fmt" ;;
180
+ lang.packer.2) _M="Packer : validate -syntax-only" ;;
181
+ lang.php.1) _M="PHP : pint" ;;
182
+ lang.php.2) _M="PHP : composer test" ;;
183
+ lang.python.1) _M="Python : ruff format" ;;
184
+ lang.python.2) _M="Python : manage.py test" ;;
185
+ lang.python.3) _M="Python : aucun test." ;;
186
+ lang.python.4) _M="Python : pytest absent, tests ignorés." ;;
187
+ lang.python.5) _M="Python : pytest" ;;
188
+ lang.python.6) _M="Python : aucun test trouvé." ;;
189
+ lang.ruby.1) _M="Ruby : rubocop -a" ;;
190
+ lang.ruby.2) _M="Ruby : standardrb --fix" ;;
191
+ lang.ruby.3) _M="Ruby : rails test" ;;
192
+ lang.rust.1) _M="Rust : rustfmt" ;;
193
+ lang.rust.2) _M="Rust : cargo test" ;;
194
+ lang.shell.1) _M="Shell : shfmt" ;;
195
+ lang.swift.1) _M="Swift : swift format" ;;
196
+ lang.swift.2) _M="Swift : swiftformat" ;;
197
+ lang.swift.3) _M="Swift : swift test" ;;
198
+ lang.tasks.1) _M="make test" ;;
199
+ lang.tasks.2) _M="just test" ;;
200
+ lang.tasks.3) _M="task test" ;;
201
+ lang.tasks.4) _M="Pas de cible \"test\" (make/just/task)." ;;
202
+ lang.terraform.1) _M="Terraform : fmt" ;;
203
+ lang.terraform.2) _M="Terraform : init -backend=false + validate" ;;
204
+ lang.terraform.3) _M="Terraform : tflint" ;;
205
+ help.alias.feat) _M="(branche : feature/ accepté)" ;;
206
+ help.alias.fix) _M="(branche : bugfix/, hotfix/ acceptés)" ;;
207
+ # CI et protection
208
+ ci.check.2) _M="Pas de base : vérification ignorée." ;;
209
+ ci.check.3) _M="%s « %s » : commit à squasher avant le merge (git rebase -i --autosquash)." ;;
210
+ ci.check.4) _M="%s « %s » : format attendu <type>(<scope>): <description>." ;;
211
+ ci.check.5) _M="%s « %s » : première ligne > 72 caractères (hors suffixe « (#NN) » de GitHub)." ;;
212
+ ci.check.6) _M="Titre de la PR « %s » : format attendu <type>(<scope>): <description> (il devient le message du commit en squash)." ;;
213
+ ci.check.7) _M="Titre de la PR « %s » : plus de 72 caractères." ;;
214
+ ci.check.8) _M="Titre de la PR conforme." ;;
215
+ ci.check.9) _M="Types :" ;;
216
+ ci.check.10) _M="Corriger : en merge squash, seul le titre de la PR devient le message final : le corriger suffit." ;;
217
+ ci.check.11) _M="Sinon (historique conservé) : git rebase -i %s (reword), puis git push --force-with-lease" ;;
218
+ ci.check.12) _M="%s commit(s) conforme(s)." ;;
219
+ ci.check.13) _M="Pas de branche (tag ou HEAD détachée) : ignoré." ;;
220
+ ci.check.14) _M="Branche « %s » non conforme : <type>/<sujet>, ex. %s" ;;
221
+ ci.check.15) _M="« %s » conforme." ;;
222
+ ci.check.16) _M="Cible « %s » conforme au flux." ;;
223
+ ci.check.17) _M="PR de « %s » vers « %s » : cible attendue %s (réglage integrationBranch)." ;;
224
+ ci.check.18) _M=" Corriger : modifier la branche de base de la PR (Edit, à côté du titre), ou gh pr edit --base %s" ;;
225
+ ci.check.19) _M="gitleaks absent : lancer ci/install-tool.sh gitleaks avant ce script." ;;
226
+ ci.check.20) _M="Dossier de travail modifié avant la vérification : impossible de contrôler le formatage." ;;
227
+ ci.check.21) _M="Un formateur a échoué (erreur de syntaxe, ou outil / dépendance indisponible) : voir le log ci-dessus." ;;
228
+ ci.check.22) _M="Corriger : installer les hooks (repowarden install --global) ou lancer le formateur, puis commiter." ;;
229
+ ci.check.23) _M="Fichiers modifiés correctement formatés." ;;
230
+ ci.check.24) _M="repowarden CI — base : %s — vérifications : %s" ;;
231
+ ci.check.25) _M="Désactivé par .repowarden.conf (skip %s)." ;;
232
+ ci.check.26) _M="Échec :%s" ;;
233
+ ci.check.27) _M="Toutes les vérifications repowarden sont passées." ;;
234
+ ci.check.inverted) _M="PR de « %s » vers « %s » : une branche persistante ne part que pour une livraison (develop → main) ou un retour (main → develop). PR sans objet : rien à vérifier, elle est fermée automatiquement." ;;
235
+ ci.check.inverted_fix) _M="Pour mettre une branche de travail à jour : sur cette branche, git merge origin/%s, puis git push." ;;
236
+ ci.check.back_merge) _M="Retour de %s dans %s (merge commit) : le titre de la PR n'est pas vérifié." ;;
237
+ ci.fix_pr.1) _M="PR #%s fermée : doublon de #%s (cible %s)" ;;
238
+ ci.fix_pr.2) _M="PR #%s reciblée : %s → %s (flux %s)" ;;
239
+ ci.fix_pr.3) _M="Titre conforme : %s" ;;
240
+ ci.fix_pr.4) _M="Titre « %s » remplacé par « %s »" ;;
241
+ ci.version.1) _M="version annoncée par la PR de livraison" ;;
242
+ ci.version.2) _M="Aucun feat, fix, perf ni changement incompatible depuis %s : pas de version." ;;
243
+ ci.version.3) _M="première version" ;;
244
+ ci.version.4) _M="%s depuis %s" ;;
245
+ ci.version.5) _M="_Depuis %s._" ;;
246
+ ci.install_tool.1) _M="%s déjà présent : %s" ;;
247
+ ci.install_tool.sha_required) _M="Miroir : aucune empreinte connue pour %s ; fournir l'empreinte SHA-256 attendue dans %s (elle ne vient jamais du miroir)." ;;
248
+ ci.install_tool.2) _M="Téléchargement de %s" ;;
249
+ ci.install_tool.3) _M="Somme SHA-256 invalide pour %s" ;;
250
+ ci.install_tool.4) _M="%s %s installé dans %s" ;;
251
+ ci.proteger.1) _M="Dépôt : %s" ;;
252
+ ci.proteger.2) _M=" branches protégées :%s" ;;
253
+ ci.proteger.3) _M=" vérifications exigées : %s" ;;
254
+ ci.proteger.4) _M=" merges : %s ; suppression auto des branches : %s" ;;
255
+ ci.proteger.5) _M=" tags v* : %s" ;;
256
+ ci.proteger.6) _M=" branche par défaut : %s" ;;
257
+ ci.proteger.7) _M=" approbations exigées : %s ; revue des CODEOWNERS : %s" ;;
258
+ ci.proteger.8) _M=" (les admins gardent un contournement explicite, tracé dans l'historique de la PR)" ;;
259
+ ci.proteger.9) _M=" environnement « %s » : approbation de %s ; branches protégées et tags v*" ;;
260
+ ci.proteger.10) _M="Ruleset « %s » mis à jour." ;;
261
+ ci.proteger.11) _M="Ruleset « %s » créé." ;;
262
+ ci.proteger.12) _M="Environnement « %s » configuré." ;;
263
+ ci.proteger.13) _M="Réglages appliqués." ;;
264
+ ci.proteger.reviews_invalid) _M="--relecteurs : nombre de 0 à 9 attendu." ;;
265
+ ci.proteger.codeowners_invalid) _M="codeOwnerReview : true ou false." ;;
266
+ ci.proteger.no_branch) _M="Aucune des branches « %s » n'existe sur %s." ;;
267
+ ci.proteger.no_cleanup) _M="Suppression automatique des branches désactivée (elle supprimerait develop) : ajouter un workflow qui appelle nettoyage-branches.yml, sinon les branches mergées restent (voir En organisation)." ;;
268
+ ci.proteger.delivery) _M=" livraison vers main : %s approbation(s) exigée(s) (passage à une nouvelle version) ; merge automatique désactivé" ;;
269
+ ci.proteger.methods_flow) _M="main : merge commit ; %s : squash ou merge commit" ;;
270
+ ci.proteger.tags_org) _M="réservés aux workflows" ;;
271
+ ci.proteger.tags_user) _M="ni déplacés ni supprimés (compte personnel : création libre)" ;;
272
+ ci.proteger.tags_none) _M="non protégés (--sans-tags)" ;;
273
+ ci.proteger.unknown_team) _M="Équipe inconnue ou inaccessible : %s (format @organisation/equipe)" ;;
274
+ ci.proteger.unknown_user) _M="Utilisateur inconnu : %s" ;;
275
+ ci.version.usage) _M="Usage : ci/version.sh next|notes [ref]" ;;
276
+ ci.version.breaking) _M="Changements incompatibles" ;;
277
+ ci.version.features) _M="Fonctionnalités" ;;
278
+ ci.version.fixes) _M="Corrections" ;;
279
+ ci.version.maintenance) _M="Maintenance" ;;
280
+ ci.install_tool.os) _M="Système non supporté : %s" ;;
281
+ ci.install_tool.arch) _M="Architecture non supportée : %s" ;;
282
+ ci.install_tool.unknown) _M="Outil inconnu : %s" ;;
283
+ # Code mort et sections de la CI
284
+ dc.no_changes) _M="Aucune modification par rapport à %s : lancer depuis une branche de travail (seul le code ajouté par la branche est analysé)." ;;
285
+ dc.section) _M="Code mort (nouveau code uniquement)" ;;
286
+ dc.no_base) _M="Pas de base : analyse du code mort ignorée." ;;
287
+ dc.proven) _M="%s : %s (code mort prouvé)" ;;
288
+ dc.candidate) _M="%s : %s (candidat : vérifier les usages dynamiques)" ;;
289
+ dc.nothing) _M="Aucun fichier d'un langage analysable parmi les modifications : rien à vérifier." ;;
290
+ dc.none) _M="Aucun nouveau code mort." ;;
291
+ dc.summary_candidates) _M="%s candidat(s) à vérifier : non bloquant (appel par réflexion, framework, API publique possibles)." ;;
292
+ dc.summary_proven) _M="%s élément(s) de code mort prouvé : à supprimer, ou à ignorer (deadcodeIgnore)." ;;
293
+ dc.summary_strict) _M="%s élément(s) de code mort (mode deadcode = strict)." ;;
294
+ dc.tool_missing) _M="%s : %s absent, code mort non analysé." ;;
295
+ dc.knip_file) _M="fichier jamais importé" ;;
296
+ dc.knip_export) _M="export jamais importé :" ;;
297
+ dc.knip_dep) _M="dépendance jamais importée :" ;;
298
+ ci.section.commits) _M="Messages de commit (Conventional Commits)" ;;
299
+ ci.section.branch) _M="Nom de branche" ;;
300
+ ci.section.secrets) _M="Secrets (gitleaks)" ;;
301
+ ci.section.format) _M="Formatage" ;;
302
+ ci.section.tests) _M="Tests des projets touchés" ;;
303
+ ci.section.strict) _M="Mode strict" ;;
304
+ # Notifications
305
+ ci.notify.sent) _M="Message envoyé au canal de l'équipe." ;;
306
+ ci.notify.failed) _M="Envoi au canal de l'équipe impossible (adresse REPOWARDEN_WEBHOOK ?) : release non affectée." ;;
307
+ ci.notify.released) _M="🚀 %s %s publiée : %s" ;;
308
+ ci.notify.waiting) _M="⏳ %s : la PR de release #%s attend une approbation : %s" ;;
309
+ ci.notify.failure) _M="❌ %s : échec du workflow de release : %s" ;;
310
+ # GitHub CLI
311
+ bot.no_gh) _M="GitHub CLI (gh) requis : https://cli.github.com" ;;
312
+ # Paquet npm
313
+ npm.npx_global) _M="npx utilise un dossier temporaire : des hooks globaux pointeraient vers un dossier effacé. Installe d'abord le paquet : npm install -g repowarden, puis repowarden install --global." ;;
314
+ npm.no_npm) _M="npm requis : https://nodejs.org" ;;
315
+ npm.no_package) _M="Aucun package.json à la racine du dépôt." ;;
316
+ npm.old_npm) _M="npm %s trop ancien : 11.15 au minimum (npm install -g npm@11)." ;;
317
+ npm.step_login) _M="1/3 Compte npm" ;;
318
+ npm.logged) _M="Connecté à npm : %s" ;;
319
+ npm.no_2fa) _M="Double authentification (2FA) non activée sur le compte npm : npm l'exige pour créer le jeton et configurer la publication sans jeton." ;;
320
+ npm.hint_2fa) _M="Activer : npm profile enable-2fa auth-and-writes (QR code pour une application d'authentification), ou https://www.npmjs.com/settings/%s/tfa ; puis relancer cette commande." ;;
321
+ npm.step_token) _M="2/3 Première publication, par la pipeline (jeton temporaire)" ;;
322
+ npm.token_exists) _M="Secret NPM_TOKEN déjà présent : réutilisé." ;;
323
+ npm.ask_password) _M="Mot de passe npm (saisie masquée, transmis à npm seulement) : " ;;
324
+ npm.ask_otp) _M="Code à usage unique (application d'authentification ou e-mail de npm) : " ;;
325
+ npm.token_failed) _M="Création du jeton npm impossible (voir ci-dessus) : rien n'a été enregistré." ;;
326
+ npm.token_stored) _M="Jeton temporaire (7 jours) créé et enregistré en secret NPM_TOKEN, sans affichage." ;;
327
+ npm.dispatched) _M="Pipeline lancée (%s) : elle publie la dernière release sur npm. Attente… (Ctrl+C : relancer la commande plus tard, elle reprendra)" ;;
328
+ npm.waiting) _M="La prochaine release publie %s : merge une PR (ou laisse le bot merger la PR de release). Attente… (Ctrl+C : relancer la commande plus tard, elle reprendra)" ;;
329
+ npm.still_waiting) _M="toujours en attente (%s min)…" ;;
330
+ npm.not_yet) _M="Pas encore publié : relance cette commande après la prochaine release." ;;
331
+ npm.first_published) _M="%s@%s publié par la pipeline." ;;
332
+ npm.step_trust) _M="3/3 Publication sans jeton (publication de confiance)" ;;
333
+ npm.trust_exists) _M="Publication de confiance déjà configurée." ;;
334
+ npm.trust_set) _M="Publication de confiance : %s, workflow %s." ;;
335
+ npm.token_removed) _M="Secret NPM_TOKEN supprimé du dépôt." ;;
336
+ npm.token_revoked) _M="Jeton temporaire révoqué sur npm." ;;
337
+ npm.done) _M="Terminé : chaque release publie %s sur npm, sans jeton." ;;
338
+ # Statut (repowarden statut)
339
+ st.hooks_on) _M="Hooks git actifs pour tous les dépôts (cette installation)" ;;
340
+ st.hooks_repo) _M="Hooks git actifs dans ce dépôt seulement (installation locale ; tous les dépôts : repowarden install --global)" ;;
341
+ st.hooks_off) _M="Hooks git non activés : aucun contrôle au commit ni au push" ;;
342
+ st.hooks_other) _M="Hooks git d'une autre installation de repowarden : %s" ;;
343
+ st.hooks_foreign) _M="Hooks git d'un autre outil (core.hooksPath = %s) : laissés tels quels" ;;
344
+ st.cc_on) _M="Assistant de commit : git cc" ;;
345
+ st.cc_off) _M="Assistant de commit (git cc) non installé" ;;
346
+ st.cc_other) _M="git cc pointe vers une autre installation de repowarden" ;;
347
+ st.cc_foreign) _M="Alias git cc déjà pris par autre chose : %s" ;;
348
+ st.lang) _M="Langue des messages : %s" ;;
349
+ st.repo) _M="Dépôt : %s" ;;
350
+ st.repo_local) _M="Hooks propres à ce dépôt (core.hooksPath local = %s), prioritaires" ;;
351
+ st.repo_lefthook) _M="%s présent : lefthook n'est plus pris en charge depuis repowarden 4, ses commandes ne sont pas lancées. Les déplacer dans .repowarden/<hook>, la version attendue dans .repowarden.conf (version)" ;;
352
+ st.repo_version_old) _M="Le projet demande repowarden %s ; installé : %s" ;;
353
+ st.repo_version_ok) _M="Version demandée par le projet (%s) : respectée" ;;
354
+ st.repo_forge) _M="Plateforme : %s" ;;
355
+ st.tools) _M="Outils présents : %s · absents : %s (selon les langages des projets)" ;;
356
+ st.next) _M="Étape suivante :" ;;
357
+ st.ready) _M="Tout est prêt : git cc pour commiter, repowarden code-mort sur une branche." ;;
358
+ st.help) _M="Toutes les commandes : repowarden --help" ;;
359
+ # Tickets
360
+ tk.none) _M="Aucun ticket relié : ajouter « Ticket : #12 » à la description de la PR, ou nommer la branche feat/12-sujet." ;;
361
+ tk.waiting) _M="Ticket %s à valider : un mainteneur pose l'étiquette « %s » sur le ticket." ;;
362
+ tk.ok) _M="Ticket %s validé." ;;
363
+ tk.merged) _M="Mergé par la PR #%s dans %s : en préprod, disponible dans la prochaine préversion." ;;
364
+ tk.branch_created) _M="Ticket validé : sa branche \`%s\` est créée depuis \`%s\`.\n\nPour y travailler :\n\n\`\`\`bash\nrepowarden ticket %s\n\`\`\`\n\n(ou \`git fetch && git switch %s\`). Le premier push ouvre la PR en brouillon." ;;
365
+ tk.branch_notice) _M="Branche %s créée pour le ticket #%s" ;;
366
+ tk.sync.ready) _M="Ticket #%s assigné : branche locale %s prête (repowarden ticket %s)." ;;
367
+ tk.sync.not_validated) _M="Ticket #%s assigné mais pas encore validé : pas de branche pour l'instant." ;;
368
+ tk.sync.failed) _M="Ticket #%s : branche non créée (droits sur le dépôt ?)." ;;
369
+ tk.sync.reassigned) _M="Ticket #%s n'est plus assigné à toi (maintenant : %s) : branche locale %s conservée." ;;
370
+ tk.sync.done) _M="Branches des tickets assignés à jour." ;;
371
+ tk.validated_waiting) _M="Ticket validé, en attente d'être pris : sa branche sera créée à l'assignation, ou en le prenant soi-même avec \`repowarden ticket %s\`." ;;
372
+ tk.pr_inverted) _M="PR fermée automatiquement : une PR de \`%s\` vers \`%s\` est sans objet. Une branche persistante ne part que pour une livraison (develop → main) ou un retour (main → develop). Pour mettre une branche de travail à jour : sur cette branche, \`git merge origin/%s\`, puis \`git push\`." ;;
373
+ tk.pr_backmerge_empty) _M="PR fermée automatiquement : \`%s\` n'apporte aucun contenu à \`%s\`. Le retour est fait automatiquement après une release, seulement quand il y a quelque chose à rapporter." ;;
374
+ tk.branch_registered) _M="Branche du ticket : \`%s\`." ;;
375
+ tk.branch_second) _M="Seconde branche pour ce ticket : \`%s\` (déjà : %s). Un ticket, une branche : regrouper le travail, ou ouvrir un ticket séparé." ;;
376
+ tk.adopted) _M="Tickets existants : %s rangé(s), %s branche(s) inscrite(s)." ;;
377
+ tk.init_adopt) _M="Tickets et branches existants" ;;
378
+ tk.abandoned) _M="Ticket #%s abandonné : PR fermée, branche supprimée." ;;
379
+ tk.branch_deleted) _M="Branche %s supprimée (ticket #%s abandonné)" ;;
380
+ tk.draft_opened) _M="PR #%s ouverte en brouillon pour le ticket #%s" ;;
381
+ tk.created_body) _M="Ticket créé automatiquement pour la PR #%s, qui n'en citait aucun. À valider par un mainteneur (étiquette « validé »)." ;;
382
+ tk.created) _M="Ticket #%s créé pour la PR #%s et relié à sa description : à valider par un mainteneur." ;;
383
+ tk.init_labels) _M="Étiquettes (statuts et validation)" ;;
384
+ tk.init_files) _M="Modèle de ticket et workflow" ;;
385
+ tk.init_exists) _M="%s existe déjà : laissé tel quel." ;;
386
+ tk.init_written) _M="%s créé." ;;
387
+ tk.init_done) _M="Suivi des tickets prêt." ;;
388
+ tk.init_next) _M="Ensuite : committer ces fichiers ; exiger la vérification « ticket » (repowarden proteger --checks \"…,ticket\") ; dans release.yml, brancher le job tickets (voir la doc Tickets)." ;;
389
+ tk.prerelease) _M="Disponible en préversion **%s** : à tester avant la production." ;;
390
+ tk.released) _M="Publié dans **%s**." ;;
391
+ *) _M="" ;;
392
+ esac
393
+ }
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+ # Langue des messages (fr, en) et traduction. Compatible bash 3.2, sans
3
+ # sous-processus.
4
+ # Poste : REPOWARDEN_LANG, sinon repowarden.lang (git config : choisie à
5
+ # l'installation, prioritaire), sinon lang de .repowarden.conf,
6
+ # sinon la langue du système.
7
+ # CI : REPOWARDEN_LANG, sinon lang de .repowarden.conf, sinon anglais
8
+ # (les logs sont lus par toute l'équipe).
9
+ # Une clé absente du catalogue de la langue retombe sur le français.
10
+ # shellcheck disable=SC2034 # REPOWARDEN_LANG utilisée par les scripts qui sourcent ce fichier
11
+
12
+ I18N_DIR="$(dirname "${BASH_SOURCE[0]}")/i18n"
13
+ # shellcheck source=i18n/fr.sh
14
+ source "$I18N_DIR/fr.sh"
15
+ # shellcheck source=i18n/en.sh
16
+ source "$I18N_DIR/en.sh"
17
+
18
+ # Valeur imposée par l'environnement, mémorisée avant toute résolution
19
+ [ -n "${REPOWARDEN_LANG_ENV+x}" ] || REPOWARDEN_LANG_ENV="${REPOWARDEN_LANG:-}"
20
+
21
+ i18n_init() {
22
+ local lang="$REPOWARDEN_LANG_ENV"
23
+ if [ -z "$lang" ]; then
24
+ if declare -F cfg_r >/dev/null; then
25
+ cfg_r lang ""
26
+ lang="$REPLY"
27
+ else
28
+ # Scripts sans la configuration complète (ci/install-tool.sh…) :
29
+ # git config, puis le .repowarden.conf du dépôt courant
30
+ # (anciens noms repogarde.lang, .repogarde.conf : lus aussi en v4)
31
+ lang="$(git config --get repowarden.lang 2>/dev/null ||
32
+ git config --get repogarde.lang 2>/dev/null ||
33
+ git config -f "$(git rev-parse --show-toplevel 2>/dev/null)/.repowarden.conf" --get repowarden.lang 2>/dev/null ||
34
+ git config -f "$(git rev-parse --show-toplevel 2>/dev/null)/.repogarde.conf" --get repogarde.lang 2>/dev/null || true)"
35
+ fi
36
+ fi
37
+ if [ -z "$lang" ]; then
38
+ if [ -n "${CI:-}" ]; then
39
+ lang=en
40
+ else
41
+ case "${LC_ALL:-${LC_MESSAGES:-${LANG:-}}}" in fr* | FR*) lang=fr ;; *) lang=en ;; esac
42
+ fi
43
+ fi
44
+ case "$lang" in fr* | FR*) REPOWARDEN_LANG=fr ;; *) REPOWARDEN_LANG=en ;; esac
45
+ }
46
+
47
+ # Traduction dans _T, sans toucher à REPLY (utilisé par les hooks pour
48
+ # transmettre des résultats entre fonctions)
49
+ _tr() {
50
+ local key="$1"
51
+ shift
52
+ _M=""
53
+ [ "${REPOWARDEN_LANG:-}" = en ] && _msg_en "$key"
54
+ [ -n "$_M" ] || _msg_fr "$key"
55
+ [ -n "$_M" ] || _M="$key"
56
+ if [ $# -gt 0 ]; then
57
+ # shellcheck disable=SC2059 # le format vient du catalogue
58
+ printf -v _T "$_M" "$@"
59
+ else
60
+ _T="$_M"
61
+ fi
62
+ }
63
+
64
+ # t <clé> [args] → REPLY (scripts interactifs : assistant, installation)
65
+ t() {
66
+ _tr "$@"
67
+ REPLY="$_T"
68
+ }
69
+
70
+ # Traduire et afficher en une fois, REPLY préservé : ok_t, info_t, step_t,
71
+ # err_t, attention_t, warn_t, dim_t, echo_t <clé> [args]
72
+ ok_t() { _tr "$@"; ok "$_T"; }
73
+ info_t() { _tr "$@"; info "$_T"; }
74
+ step_t() { _tr "$@"; step "$_T"; }
75
+ err_t() { _tr "$@"; err "$_T"; }
76
+ attention_t() { _tr "$@"; attention "$_T"; }
77
+ warn_t() { _tr "$@"; warn "$_T"; }
78
+ dim_t() { _tr "$@"; dim "$_T"; }
79
+ echo_t() { _tr "$@"; echo "$_T"; }
80
+
81
+ i18n_init