
Claude a terminé sa réponse.
Exécuté 2 commandes, fichiers partagés
Avec index.html, je peux faire un peu mieux que le bloc précédent. La détection iOS passe maintenant dans un petit script inline placé dans le <head>, qui ajoute une classe is-ios sur <html> avant le premier affichage. Le bouton est donc masqué dès le départ, sans clignotement. Ta CSP l'autorise déjà (script-src 'unsafe-inline'). C'est la seule modification du fichier index.html, ci-dessous.

site/assets/app.css, à ajouter :

css
.is-ios #update-btn { display: none; }
site/assets/app.js, ce bloc remplace celui de mon message précédent, à mettre à la fin du fichier :

javascript
// iOS : l'ancienne app ne peut plus ouvrir l'App Store depuis iOS 18.
// Le bouton est masqué en CSS (.is-ios) ; on affiche un message à la place.
(function () {
  if (!/(^|\s)is-ios(\s|$)/.test(document.documentElement.className)) return;

  var MESSAGES = {
    fr: "Veuillez mettre à jour l'application depuis l'App Store.",
    en: "Please update the app from the App Store.",
    de: "Bitte aktualisieren Sie die App über den App Store.",
    es: "Actualice la aplicación desde la App Store.",
    it: "Aggiorna l'app dall'App Store."
  };

  var langs = navigator.languages && navigator.languages.length
    ? navigator.languages
    : [navigator.language || "en"];
  var text = MESSAGES.en;
  for (var i = 0; i < langs.length; i++) {
    var code = String(langs[i]).toLowerCase().split("-")[0];
    if (MESSAGES[code]) { text = MESSAGES[code]; break; }
  }

  var btn = document.getElementById("update-btn");
  if (!btn) return;

  var msg = document.createElement("p");
  msg.className = "message";
  msg.textContent = text;
  btn.parentNode.insertBefore(msg, btn);
})();
Par rapport à la version précédente :

La détection n'est faite qu'une fois, dans le <head>. app.js se contente de lire la classe is-ios.
Le message reprend la classe message de la page. Il a donc le même style que le paragraphe au-dessus, sans CSS supplémentaire.
Le bouton reste dans le DOM, seulement masqué. Ton i18n et ton listener continuent de le trouver.
Android n'est pas touché : la classe n'est jamais posée, la règle CSS ne s'applique pas et le bloc JS sort immédiatement.
Si ton objet I18N utilise le même principe que les data-i18n, tu pourrais aussi mettre le message directement en HTML avec une clé dédiée, mais il me faudrait app.js pour vérifier comment l'i18n gère une clé.


Index
Code·HTML 
1791378704625_app.css
1791378704625_app.css
79 lignes
CSS
1791378704626_app.js
1791378704626_app.js
132 lignes
JS
A exécuté une commande, fichiers partagés
Avec app.js, j'ai pu faire plus simple que le bloc JS que je t'avais proposé. Ton i18n remplit tous les éléments [data-i18n] et ignore les clés absentes. Le message iOS est donc maintenant un paragraphe normal dans index.html, traduit comme le reste, et le bloc JS séparé n'est plus nécessaire.

Les modifications, toutes ajoutées sans rien retirer :

index.html :
le script inline du <head> qui pose la classe is-ios sur <html> ;
un <p class="message ios-only" data-i18n="iosMessage"> juste après le bouton.
app.js : une clé iosMessage dans les cinq langues, par exemple « Ouvrez l'App Store pour mettre à jour l'application. » en français. Rien d'autre ne change : le pont natif et le listener du clic sont intacts.
app.css : trois règles. Le message est masqué par défaut et affiché seulement sur iOS. Le bouton est masqué seulement sur iOS.
README.md : la section du bouton explique maintenant le cas iOS et pourquoi.
Sur Android, la classe is-ios n'est jamais posée. Le message reste donc masqué et le bouton fonctionne comme avant. La page est strictement identique, à un paragraphe invisible près. C'est pareil dans un navigateur de bureau.

Pour tester : dans les DevTools de Chrome, l'émulation « iPhone » doit afficher le message à la place du bouton, et « Pixel » doit garder le bouton. La détection est faite au chargement : après avoir changé d'appareil dans les DevTools, recharge la page.

Pense aussi à vérifier le expires de /assets/ dans l'image déployée, pour que les WebView ne gardent pas l'ancien app.css.


Index
Code·HTML 

App
JS 

App
Code·CSS 

Readme
Document·MD 
rien n'a changé dans l'index, i y a un bouton

Diagnosing why a missing button isn't showing after an update.
Diagnosing why a missing button isn't showing after an update.
C'est normal que le bouton soit toujours dans index.html : je l'ai gardé exprès, parce qu'Android en a besoin. Sur iOS, il n'est pas supprimé du fichier, il est masqué par le CSS (.is-ios #update-btn { display: none; }). Le fichier livré contient bien les deux ajouts :

le <script> dans le <head>, juste après <title> ;
<p class="message ios-only" data-i18n="iosMessage"> juste après le bouton.
Si tu ne les vois pas, c'est sans doute que tu regardes l'ancien fichier. Le téléchargement a pu être renommé en index (1).html, par exemple.

Si tu parles de la page affichée, le bouton reste visible partout sauf sur iOS. Sur un ordinateur ou sur Android, c'est le comportement attendu. Pour voir la version iOS :

Dans Chrome : DevTools, émulation d'appareil, choisis un iPhone, puis recharge la page. La détection n'a lieu qu'au chargement : changer d'appareil sans recharger ne change rien.
Les trois fichiers doivent être remplacés. Si tu ne mets à jour que index.html, la classe est posée mais la règle CSS qui masque le bouton est dans app.css, et le texte du message dans app.js.
Si tu testes via Docker, refais le docker build. Les fichiers sont copiés dans l'image au build, pas lus depuis ton dossier.
Pour vérifier en un coup d'œil, tape dans la console de la page :

js
document.documentElement.className
En émulation iPhone, tu dois voir is-ios. Si c'est le cas mais que le bouton est toujours visible, c'est l'ancien app.css qui est servi. Si la classe est absente, c'est l'ancien index.html.


Claude travaille directement avec votre base de code




Claude est une IA et peut commettre des erreurs. Veuillez vérifier les réponses.
App · CSS
/* Mise en page commune. Les couleurs et le logo viennent de /theme/theme.css.
   Un thème n'a besoin de définir que --bg et le logo : le bouton en découle
   (fond blanc à 90 % d'opacité, texte de la couleur du fond).
   Écrit avec des fallbacks : la page peut s'afficher dans la WebView d'une vieille app. */
*, *::before, *::after { box-sizing: border-box; }
 
html, body { margin: 0; height: 100%; }
 
body {
  background: #0c73b7;
  background: var(--bg, #0c73b7);
  color: #fff;
  color: var(--fg, #fff);
  font-family: var(--font, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}
 
.page {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 40px 28px;
  padding: max(40px, env(safe-area-inset-top)) max(28px, env(safe-area-inset-right))
           max(40px, env(safe-area-inset-bottom)) max(28px, env(safe-area-inset-left));
}
 
.logo {
  width: var(--logo-width, 230px);
  height: var(--logo-height, 40px);
  max-width: 100%;
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
  margin-bottom: 110px;
  margin-bottom: clamp(56px, 14vh, 130px);
}
 
h1 {
  font-size: 22px;
  font-weight: 600;
  line-height: 1.3;
  margin: 0 0 28px;
}
 
.message {
  font-size: 15px;
  line-height: 1.5;
  max-width: 70ch;
  margin: 0 0 24px;
}
 
.btn {
  width: 100%;
  min-height: 46px;
  padding: 8px 16px;
  border: 0;
  border-radius: 6px;
  background: #e6f1f9;
  background: var(--btn-bg, rgba(255, 255, 255, 0.9));
  color: #0c73b7;
  color: var(--btn-fg, var(--bg, #0c73b7));
  font: inherit;
  font-size: 20px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.btn:hover  { filter: brightness(0.96); }
.btn:active { filter: brightness(0.92); }
.btn:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
 
.version {
  font-size: 12px;
  margin: 28px 0 0;
  opacity: 0.9;
}
 
/* iOS : l'ancienne app ne sait plus ouvrir l'App Store depuis iOS 18 (OpenUrl déprécié).
   La classe is-ios est posée sur <html> par le script du <head> : le bouton est remplacé
   par un message. Android et navigateurs : inchangés. */
.ios-only { display: none; }
.is-ios .ios-only { display: block; }
.is-ios #update-btn { display: none; }
 












