Fix: localize the PWA install prompt

The PWA install prompt was hardcoded English regardless of the selected UI
language (and the large English block tripped Chrome's translate popup on
Android). Add a pwa_install namespace to all 17 locales, switch the component
to useTranslations, and move <PWAInstallPrompt /> from (main)/layout into
(main)/[locale]/layout so it renders inside the IntlProvider. The title keeps
the dynamic {appName}, so per-domain branding still applies.
This commit is contained in:
dealerweb
2026-05-30 15:35:05 +02:00
committed by Linus Rath
parent f0d87d594a
commit 229992853b
20 changed files with 146 additions and 8 deletions
+2
View File
@@ -9,6 +9,7 @@ import { ProtocolLaunchHandlerProvider } from "@/components/protocol/protocol-la
import { ProInterfaceRedirect } from "@/components/pro/pro-interface-redirect"; import { ProInterfaceRedirect } from "@/components/pro/pro-interface-redirect";
import { PluginDialogHost } from "@/components/plugins/plugin-dialog-host"; import { PluginDialogHost } from "@/components/plugins/plugin-dialog-host";
import { PluginConsentDialog } from "@/components/plugins/plugin-consent-dialog"; import { PluginConsentDialog } from "@/components/plugins/plugin-consent-dialog";
import { PWAInstallPrompt } from "@/components/pwa-install-prompt";
import { locales } from "@/i18n/routing"; import { locales } from "@/i18n/routing";
export default async function LocaleLayout({ export default async function LocaleLayout({
@@ -41,6 +42,7 @@ export default async function LocaleLayout({
{children} {children}
<PluginDialogHost /> <PluginDialogHost />
<PluginConsentDialog /> <PluginConsentDialog />
<PWAInstallPrompt />
</ProtocolLaunchHandlerProvider> </ProtocolLaunchHandlerProvider>
</TourProvider> </TourProvider>
</EmbeddedBridgeProvider> </EmbeddedBridgeProvider>
-2
View File
@@ -2,7 +2,6 @@ import type { Metadata, Viewport } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { getLocale } from "next-intl/server"; import { getLocale } from "next-intl/server";
import { PWAInstallPrompt } from "@/components/pwa-install-prompt";
import { ServiceWorkerRegistration } from "@/components/service-worker-registration"; import { ServiceWorkerRegistration } from "@/components/service-worker-registration";
import { configManager } from "@/lib/admin/config-manager"; import { configManager } from "@/lib/admin/config-manager";
import { withBasePath } from "@/lib/browser-navigation"; import { withBasePath } from "@/lib/browser-navigation";
@@ -92,7 +91,6 @@ export default async function RootLayout({
> >
<ServiceWorkerRegistration /> <ServiceWorkerRegistration />
{children} {children}
<PWAInstallPrompt />
</body> </body>
</html> </html>
); );
+8 -6
View File
@@ -1,6 +1,7 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useTranslations } from "next-intl";
import { X, Download } from "lucide-react"; import { X, Download } from "lucide-react";
import { useConfig } from "@/hooks/use-config"; import { useConfig } from "@/hooks/use-config";
import { withBasePath } from "@/lib/browser-navigation"; import { withBasePath } from "@/lib/browser-navigation";
@@ -17,6 +18,7 @@ export function PWAInstallPrompt() {
useState<BeforeInstallPromptEvent | null>(null); useState<BeforeInstallPromptEvent | null>(null);
const [showPrompt, setShowPrompt] = useState(false); const [showPrompt, setShowPrompt] = useState(false);
const { appName, faviconUrl, appLogoLightUrl, appLogoDarkUrl } = useConfig(); const { appName, faviconUrl, appLogoLightUrl, appLogoDarkUrl } = useConfig();
const t = useTranslations("pwa_install");
useEffect(() => { useEffect(() => {
if (localStorage.getItem(DISMISSED_KEY)) return; if (localStorage.getItem(DISMISSED_KEY)) return;
@@ -84,17 +86,17 @@ export function PWAInstallPrompt() {
)} )}
<div> <div>
<h3 className="font-semibold text-sm text-neutral-900 dark:text-white"> <h3 className="font-semibold text-sm text-neutral-900 dark:text-white">
Install {appName} {t("title", { appName })}
</h3> </h3>
<p className="text-xs text-neutral-600 dark:text-neutral-400 mt-1"> <p className="text-xs text-neutral-600 dark:text-neutral-400 mt-1">
Install our app for quick access and offline support. {t("description")}
</p> </p>
</div> </div>
</div> </div>
<button <button
onClick={handleDismiss} onClick={handleDismiss}
className="text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-300 transition-colors" className="text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-300 transition-colors"
aria-label="Dismiss install prompt" aria-label={t("dismiss_aria")}
> >
<X className="w-4 h-4" /> <X className="w-4 h-4" />
</button> </button>
@@ -105,20 +107,20 @@ export function PWAInstallPrompt() {
onClick={handleDismiss} onClick={handleDismiss}
className="flex-1 px-3 py-2 text-sm font-medium text-neutral-700 dark:text-neutral-300 bg-neutral-100 dark:bg-neutral-800 rounded hover:bg-neutral-200 dark:hover:bg-neutral-700 transition-colors" className="flex-1 px-3 py-2 text-sm font-medium text-neutral-700 dark:text-neutral-300 bg-neutral-100 dark:bg-neutral-800 rounded hover:bg-neutral-200 dark:hover:bg-neutral-700 transition-colors"
> >
Not now {t("not_now")}
</button> </button>
<button <button
onClick={handleInstall} onClick={handleInstall}
className="flex-1 px-3 py-2 text-sm font-medium text-white bg-blue-600 rounded hover:bg-blue-700 transition-colors" className="flex-1 px-3 py-2 text-sm font-medium text-white bg-blue-600 rounded hover:bg-blue-700 transition-colors"
> >
Install {t("install")}
</button> </button>
</div> </div>
<button <button
onClick={handleDismissForever} onClick={handleDismissForever}
className="w-full text-xs text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-300 transition-colors text-center" className="w-full text-xs text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-300 transition-colors text-center"
> >
Don&apos;t remind me again {t("dont_remind")}
</button> </button>
</div> </div>
</div> </div>
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Nainstalovat {appName}",
"description": "Nainstalujte si naši aplikaci pro rychlý přístup a offline podporu.",
"not_now": "Teď ne",
"install": "Nainstalovat",
"dont_remind": "Už mi to nepřipomínat",
"dismiss_aria": "Zavřít výzvu k instalaci"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "E-mail", "username_label": "E-mail",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Installer {appName}",
"description": "Installer vores app for hurtig adgang og offline-understøttelse.",
"not_now": "Ikke nu",
"install": "Installer",
"dont_remind": "Påmind mig ikke igen",
"dismiss_aria": "Afvis installationsprompt"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "Email", "username_label": "Email",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "{appName} installieren",
"description": "Installiere unsere App für schnellen Zugriff und Offline-Unterstützung.",
"not_now": "Nicht jetzt",
"install": "Installieren",
"dont_remind": "Nicht mehr erinnern",
"dismiss_aria": "Installationshinweis schließen"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "E-Mail", "username_label": "E-Mail",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Install {appName}",
"description": "Install our app for quick access and offline support.",
"not_now": "Not now",
"install": "Install",
"dont_remind": "Don't remind me again",
"dismiss_aria": "Dismiss install prompt"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "Email", "username_label": "Email",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Instalar {appName}",
"description": "Instala nuestra app para un acceso rápido y soporte sin conexión.",
"not_now": "Ahora no",
"install": "Instalar",
"dont_remind": "No volver a recordármelo",
"dismiss_aria": "Cerrar aviso de instalación"
},
"login": { "login": {
"title": "Correo Web", "title": "Correo Web",
"username_label": "Correo electrónico", "username_label": "Correo electrónico",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Installer {appName}",
"description": "Installez notre application pour un accès rapide et un support hors ligne.",
"not_now": "Pas maintenant",
"install": "Installer",
"dont_remind": "Ne plus me le rappeler",
"dismiss_aria": "Fermer l'invite d'installation"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "Email", "username_label": "Email",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Installa {appName}",
"description": "Installa la nostra app per un accesso rapido e il supporto offline.",
"not_now": "Non ora",
"install": "Installa",
"dont_remind": "Non ricordarmelo più",
"dismiss_aria": "Chiudi avviso di installazione"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "Email", "username_label": "Email",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "{appName} をインストール",
"description": "素早いアクセスとオフライン対応のため、アプリをインストールしましょう。",
"not_now": "後で",
"install": "インストール",
"dont_remind": "今後表示しない",
"dismiss_aria": "インストールプロンプトを閉じる"
},
"login": { "login": {
"title": "ウェブメール", "title": "ウェブメール",
"username_label": "メールアドレス", "username_label": "メールアドレス",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "{appName} 설치",
"description": "빠른 액세스와 오프라인 지원을 위해 앱을 설치하세요.",
"not_now": "나중에",
"install": "설치",
"dont_remind": "다시 알리지 않음",
"dismiss_aria": "설치 프롬프트 닫기"
},
"login": { "login": {
"title": "웹메일", "title": "웹메일",
"username_label": "이메일", "username_label": "이메일",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Instalēt {appName}",
"description": "Instalējiet mūsu lietotni ātrai piekļuvei un bezsaistes atbalstam.",
"not_now": "Ne tagad",
"install": "Instalēt",
"dont_remind": "Vairs man neatgādināt",
"dismiss_aria": "Aizvērt instalēšanas paziņojumu"
},
"login": { "login": {
"title": "Tīmekļa pasts", "title": "Tīmekļa pasts",
"username_label": "E-pasta adrese", "username_label": "E-pasta adrese",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "{appName} installeren",
"description": "Installeer onze app voor snelle toegang en offline-ondersteuning.",
"not_now": "Niet nu",
"install": "Installeren",
"dont_remind": "Niet meer herinneren",
"dismiss_aria": "Installatiemelding sluiten"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "E-mail", "username_label": "E-mail",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Zainstaluj {appName}",
"description": "Zainstaluj naszą aplikację, aby uzyskać szybki dostęp i obsługę offline.",
"not_now": "Nie teraz",
"install": "Zainstaluj",
"dont_remind": "Nie przypominaj mi więcej",
"dismiss_aria": "Zamknij monit instalacji"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "E-mail", "username_label": "E-mail",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Instalar {appName}",
"description": "Instale o nosso app para acesso rápido e suporte offline.",
"not_now": "Agora não",
"install": "Instalar",
"dont_remind": "Não lembrar novamente",
"dismiss_aria": "Dispensar aviso de instalação"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "E-mail", "username_label": "E-mail",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Установить {appName}",
"description": "Установите наше приложение для быстрого доступа и работы офлайн.",
"not_now": "Не сейчас",
"install": "Установить",
"dont_remind": "Больше не напоминать",
"dismiss_aria": "Закрыть запрос на установку"
},
"login": { "login": {
"title": "Веб-почта", "title": "Веб-почта",
"username_label": "Электронная почта", "username_label": "Электронная почта",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "{appName} uygulamasını yükle",
"description": "Hızlı erişim ve çevrimdışı destek için uygulamamızı yükleyin.",
"not_now": "Şimdi değil",
"install": "Yükle",
"dont_remind": "Bir daha hatırlatma",
"dismiss_aria": "Yükleme istemini kapat"
},
"login": { "login": {
"title": "Webmail", "title": "Webmail",
"username_label": "E-posta", "username_label": "E-posta",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "Встановити {appName}",
"description": "Встановіть наш застосунок для швидкого доступу та офлайн-підтримки.",
"not_now": "Не зараз",
"install": "Встановити",
"dont_remind": "Більше не нагадувати",
"dismiss_aria": "Закрити запит на встановлення"
},
"login": { "login": {
"title": "Веб-пошта", "title": "Веб-пошта",
"username_label": "Електронна пошта", "username_label": "Електронна пошта",
+8
View File
@@ -1,4 +1,12 @@
{ {
"pwa_install": {
"title": "安装 {appName}",
"description": "安装我们的应用,以获得快速访问和离线支持。",
"not_now": "暂不",
"install": "安装",
"dont_remind": "不再提醒",
"dismiss_aria": "关闭安装提示"
},
"login": { "login": {
"title": "网页邮箱", "title": "网页邮箱",
"username_label": "邮箱地址", "username_label": "邮箱地址",