diff --git a/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs b/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs
index fa6deab..563b85e 100644
--- a/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs
+++ b/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs
@@ -128,7 +128,9 @@ public static partial class NettoyageIsbd
var pointRole = IndexDuPointDeRole(s);
if (pointRole >= 0)
{
- s = s[..pointRole];
+ // Quand le rôle suit une initiale (« Cormen, Thomas H. Auteur du texte »), le point
+ // qui sert de séparateur appartient aussi au nom : on le conserve.
+ s = EstPointDInitiale(s, pointRole) ? s[..(pointRole + 1)] : s[..pointRole];
}
s = RetirerPonctuationFinale(s);
@@ -174,12 +176,7 @@ public static partial class NettoyageIsbd
while (s.EndsWith('.'))
{
- var i = s.Length - 1;
- var estInitiale = i >= 1
- && char.IsUpper(s[i - 1])
- && (i == 1 || s[i - 2] == ' ' || s[i - 2] == '-' || s[i - 2] == '\'');
-
- if (estInitiale)
+ if (EstPointDInitiale(s, s.Length - 1))
{
break;
}
@@ -194,9 +191,15 @@ public static partial class NettoyageIsbd
/// Position du point qui introduit le rôle, ou -1.
///
///
- /// Un point suivi d'une espace sépare le rôle du nom. On ignore les points d'initiales
- /// (« Cormen, Thomas H. Auteur ») : un point précédé d'une majuscule isolée n'est pas
- /// un séparateur de rôle.
+ /// Un point suivi d'une espace sépare le rôle du nom. Le point d'une initiale
+ /// (« Cormen, Thomas H. ») n'en est pas un — le couper amputerait le prénom.
+ ///
+ /// Mais protéger l'initiale ne doit pas protéger le rôle qui la suit : « Cormen, Thomas H.
+ /// Auteur du texte » renvoyait auparavant « Thomas H. Auteur du texte Cormen ». Ce qui
+ /// tranche : dans « Nom, Prénom », la seule chose qui puisse suivre une initiale et faire
+ /// encore partie du nom est une autre initiale (« Tolkien, J. R. R. »). Tout le
+ /// reste est le rôle.
+ ///
///
private static int IndexDuPointDeRole(string s)
{
@@ -207,16 +210,31 @@ public static partial class NettoyageIsbd
continue;
}
- var estInitiale = i >= 1
- && char.IsUpper(s[i - 1])
- && (i == 1 || s[i - 2] == ' ' || s[i - 2] == '-' || s[i - 2] == '\'');
-
- if (!estInitiale)
+ if (EstPointDInitiale(s, i) && CommenceParUneInitiale(s, i + 2))
{
- return i;
+ continue;
}
+
+ return i;
}
return -1;
}
+
+ ///
+ /// Le point en clôt-il une initiale (majuscule isolée) ?
+ ///
+ private static bool EstPointDInitiale(string s, int i)
+ => i >= 1
+ && char.IsUpper(s[i - 1])
+ && (i == 1 || s[i - 2] == ' ' || s[i - 2] == '-' || s[i - 2] == '\'');
+
+ ///
+ /// Le texte à partir de commence-t-il par une initiale
+ /// (une majuscule suivie d'un point ou de la fin de chaîne) ?
+ ///
+ private static bool CommenceParUneInitiale(string s, int debut)
+ => debut < s.Length
+ && char.IsUpper(s[debut])
+ && (debut + 1 == s.Length || s[debut + 1] == '.');
}
diff --git a/MaBibli.Tests/NettoyageIsbdTests.cs b/MaBibli.Tests/NettoyageIsbdTests.cs
index 493af12..7c11296 100644
--- a/MaBibli.Tests/NettoyageIsbdTests.cs
+++ b/MaBibli.Tests/NettoyageIsbdTests.cs
@@ -85,12 +85,17 @@ public class NettoyageIsbdTests
public void Auteur_laisse_passer_ce_qui_est_deja_propre(string brut, string attendu)
=> Assert.Equal(attendu, NettoyageIsbd.Auteur(brut));
- [Fact]
- public void Auteur_ne_coupe_pas_sur_une_initiale()
- {
- // « H. » est une initiale, pas un séparateur de rôle : la couper amputerait le prénom.
- Assert.Equal("Thomas H. Cormen", NettoyageIsbd.Auteur("Cormen, Thomas H."));
- }
+ [Theory]
+ // Contre-exemples : le nom se termine légitimement par une initiale, il n'y a pas de rôle
+ // à retirer — couper au point amputerait le prénom.
+ [InlineData("Cormen, Thomas H.", "Thomas H. Cormen")]
+ [InlineData("Tolkien, J. R. R.", "J. R. R. Tolkien")]
+ // Cas réel : le rôle suit une initiale. Protéger le point de l'initiale ne doit pas
+ // protéger le rôle — sinon on obtient « Thomas H. Auteur du texte Cormen ».
+ [InlineData("Cormen, Thomas H. Auteur du texte", "Thomas H. Cormen")]
+ [InlineData("Tolkien, J. R. R. (1892-1973). Auteur du texte", "J. R. R. Tolkien")]
+ public void Auteur_distingue_une_initiale_d_un_role(string brut, string attendu)
+ => Assert.Equal(attendu, NettoyageIsbd.Auteur(brut));
[Theory]
[InlineData("le Livre de poche (Paris)", "le Livre de poche")]