fix(i18n): complete translations for production launch and fix CMS store context
Some checks failed
CI / ruff (push) Successful in 10s
CI / validate (push) Successful in 26s
CI / dependency-scanning (push) Successful in 28s
CI / pytest (push) Failing after 3h13m27s
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled

- Replace CMS custom get_store_context() with core utility (same fix as loyalty)
- Add 85 missing translation keys across fr/de/lb for core, tenancy, messaging,
  customers, and loyalty modules
- Convert 21 client-side $t() calls to server-side _() in 9 loyalty templates
- Fix 3 broken translation keys in store/cards.html

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 23:38:54 +01:00
parent bc7431943a
commit 6c07f6cbb2
27 changed files with 189 additions and 82 deletions

View File

@@ -20,7 +20,8 @@ from app.modules.cms.services import content_page_service
from app.modules.core.services.platform_settings_service import (
platform_settings_service, # MOD-004 - shared platform service
)
from app.modules.tenancy.models import Store, User
from app.modules.core.utils.page_context import get_store_context
from app.modules.tenancy.models import User
from app.templates_config import templates
logger = logging.getLogger(__name__)
@@ -33,54 +34,6 @@ ROUTE_CONFIG = {
}
# ============================================================================
# HELPER: Build Store Dashboard Context
# ============================================================================
def get_store_context(
request: Request,
db: Session,
current_user: User,
store_code: str,
**extra_context,
) -> dict:
"""
Build template context for store dashboard pages.
Resolves locale/currency using the platform settings service with
store override support.
"""
# Load store from database
store = db.query(Store).filter(Store.subdomain == store_code).first()
# Get platform defaults
platform_config = platform_settings_service.get_storefront_config(db)
# Resolve with store override
storefront_locale = platform_config["locale"]
storefront_currency = platform_config["currency"]
if store and store.storefront_locale:
storefront_locale = store.storefront_locale
context = {
"request": request,
"user": current_user,
"store": store,
"store_code": store_code,
"storefront_locale": storefront_locale,
"storefront_currency": storefront_currency,
"dashboard_language": store.dashboard_language if store else "en",
}
# Add any extra context
if extra_context:
context.update(extra_context)
return context
# ============================================================================
# CONTENT PAGES MANAGEMENT
# ============================================================================

View File

@@ -65,8 +65,16 @@
"profile_updated": "Profil erfolgreich aktualisiert"
},
"messages": {
"failed_to_load_dashboard_data": "Failed to load dashboard data",
"dashboard_refreshed": "Dashboard refreshed"
"failed_to_load_dashboard_data": "Dashboard-Daten konnten nicht geladen werden",
"dashboard_refreshed": "Dashboard aktualisiert",
"item_removed_from_cart": "Artikel aus dem Warenkorb entfernt",
"cart_cleared": "Warenkorb geleert"
},
"confirmations": {
"show_all_menu_items": "Alle Menüpunkte werden angezeigt. Fortfahren?",
"hide_all_menu_items": "Alle Menüpunkte werden ausgeblendet (außer obligatorische). Sie können dann die gewünschten aktivieren. Fortfahren?",
"reset_email_settings": "Alle E-Mail-Einstellungen werden auf .env-Standardwerte zurückgesetzt. Fortfahren?",
"cleanup_logs": "Alle Protokolle, die älter als {days} Tage sind, werden gelöscht. Fortfahren?"
},
"menu": {
"dashboard": "Dashboard",

View File

@@ -65,8 +65,16 @@
"profile_updated": "Profil mis à jour avec succès"
},
"messages": {
"failed_to_load_dashboard_data": "Failed to load dashboard data",
"dashboard_refreshed": "Dashboard refreshed"
"failed_to_load_dashboard_data": "Échec du chargement des données du tableau de bord",
"dashboard_refreshed": "Tableau de bord actualisé",
"item_removed_from_cart": "Article retiré du panier",
"cart_cleared": "Panier vidé"
},
"confirmations": {
"show_all_menu_items": "Ceci affichera tous les éléments de menu. Continuer ?",
"hide_all_menu_items": "Ceci masquera tous les éléments de menu (sauf les obligatoires). Vous pourrez ensuite activer ceux que vous souhaitez. Continuer ?",
"reset_email_settings": "Ceci réinitialisera tous les paramètres e-mail aux valeurs par défaut .env. Continuer ?",
"cleanup_logs": "Ceci supprimera tous les journaux de plus de {days} jours. Continuer ?"
},
"menu": {
"dashboard": "Tableau de bord",

View File

@@ -65,8 +65,16 @@
"profile_updated": "Profil erfollegräich aktualiséiert"
},
"messages": {
"failed_to_load_dashboard_data": "Failed to load dashboard data",
"dashboard_refreshed": "Dashboard refreshed"
"failed_to_load_dashboard_data": "Dashboard-Donnéeë konnten net geluede ginn",
"dashboard_refreshed": "Dashboard aktualiséiert",
"item_removed_from_cart": "Artikel aus dem Kuerf ewechgeholl",
"cart_cleared": "Kuerf eidel gemaach"
},
"confirmations": {
"show_all_menu_items": "All Menüpunkten ginn ugewisen. Weidermaachen?",
"hide_all_menu_items": "All Menüpunkten ginn verstopp (ausser obligatoresch). Dir kënnt dann déi gewënscht aktivéieren. Weidermaachen?",
"reset_email_settings": "All E-Mail-Astellunge ginn op .env-Standardwäerter zréckgesat. Weidermaachen?",
"cleanup_logs": "All Protokoller, déi méi al wéi {days} Deeg sinn, ginn geläscht. Weidermaachen?"
},
"menu": {
"dashboard": "Dashboard",

View File

@@ -32,6 +32,11 @@
"description": "Nachrichten an Kunden senden"
}
},
"messages": {
"failed_to_toggle_customer_status": "Kundenstatus konnte nicht geändert werden",
"failed_to_load_customer_details": "Kundendetails konnten nicht geladen werden",
"failed_to_load_customer_orders": "Kundenbestellungen konnten nicht geladen werden"
},
"menu": {
"store_operations": "Shop-Betrieb",
"customers_section": "Kunden",

View File

@@ -32,6 +32,11 @@
"description": "Envoyer des messages aux clients"
}
},
"messages": {
"failed_to_toggle_customer_status": "Échec du changement de statut du client",
"failed_to_load_customer_details": "Échec du chargement des détails du client",
"failed_to_load_customer_orders": "Échec du chargement des commandes du client"
},
"menu": {
"store_operations": "Opérations du magasin",
"customers_section": "Clients",

View File

@@ -32,6 +32,11 @@
"description": "Noriichten u Clienten schécken"
}
},
"messages": {
"failed_to_toggle_customer_status": "Clientestatus konnt net geännert ginn",
"failed_to_load_customer_details": "Clientedetailer konnten net geluede ginn",
"failed_to_load_customer_orders": "Clientebestellunge konnten net geluede ginn"
},
"menu": {
"store_operations": "Buttek-Operatiounen",
"customers_section": "Clienten",

View File

@@ -78,6 +78,7 @@
"terminal": "Terminal",
"customer_cards": "Kundenkarten",
"statistics": "Statistiken",
"program": "Programm",
"overview": "Übersicht",
"settings": "Einstellungen"
},

View File

@@ -78,6 +78,7 @@
"terminal": "Terminal",
"customer_cards": "Clientekaarten",
"statistics": "Statistiken",
"program": "Programm",
"overview": "Iwwersiicht",
"settings": "Astellungen"
},

View File

@@ -13,7 +13,7 @@
{% block content %}
{% call detail_page_header("merchant?.name || 'Merchant Loyalty'", '/admin/loyalty/programs', subtitle_show='merchant') %}
<span x-text="program ? $t('loyalty.admin.merchant_detail.program_active') : $t('loyalty.admin.merchant_detail.no_program_subtitle')"></span>
<span x-text="program ? '{{ _('loyalty.admin.merchant_detail.program_active')|replace("'", "\\'") }}' : '{{ _('loyalty.admin.merchant_detail.no_program_subtitle')|replace("'", "\\'") }}'"></span>
{% endcall %}
{{ loading_state(_('loyalty.admin.merchant_detail.loading')) }}
@@ -205,14 +205,14 @@
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase mb-2">{{ _('loyalty.admin.merchant_detail.self_enrollment') }}</p>
<span class="inline-flex items-center px-2 py-1 text-xs font-semibold leading-tight rounded-full"
:class="settings?.allow_self_enrollment ? 'text-green-700 bg-green-100 dark:bg-green-700 dark:text-green-100' : 'text-gray-700 bg-gray-100 dark:bg-gray-700 dark:text-gray-100'">
<span x-text="settings?.allow_self_enrollment ? $t('loyalty.admin.merchant_detail.allowed') : $t('loyalty.admin.merchant_detail.disabled')"></span>
<span x-text="settings?.allow_self_enrollment ? '{{ _('loyalty.admin.merchant_detail.allowed')|replace("'", "\\'") }}' : '{{ _('loyalty.admin.merchant_detail.disabled')|replace("'", "\\'") }}'"></span>
</span>
</div>
<div>
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase mb-2">{{ _('loyalty.admin.merchant_detail.cross_location_redemption') }}</p>
<span class="inline-flex items-center px-2 py-1 text-xs font-semibold leading-tight rounded-full"
:class="settings?.allow_cross_location_redemption !== false ? 'text-green-700 bg-green-100 dark:bg-green-700 dark:text-green-100' : 'text-gray-700 bg-gray-100 dark:bg-gray-700 dark:text-gray-100'">
<span x-text="settings?.allow_cross_location_redemption !== false ? $t('loyalty.admin.merchant_detail.allowed') : $t('loyalty.admin.merchant_detail.disabled')"></span>
<span x-text="settings?.allow_cross_location_redemption !== false ? '{{ _('loyalty.admin.merchant_detail.allowed')|replace("'", "\\'") }}' : '{{ _('loyalty.admin.merchant_detail.disabled')|replace("'", "\\'") }}'"></span>
</span>
</div>
</div>

View File

@@ -170,7 +170,7 @@
:disabled="saving"
class="flex items-center px-4 py-2 text-sm font-medium text-white bg-purple-600 border border-transparent rounded-lg hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple disabled:opacity-50">
<span x-show="saving" x-html="$icon('spinner', 'w-4 h-4 mr-2 animate-spin')"></span>
<span x-text="saving ? $t('loyalty.common.saving') : $t('loyalty.admin.merchant_settings.save_settings')"></span>
<span x-text="saving ? '{{ _('loyalty.common.saving')|replace("'", "\\'") }}' : '{{ _('loyalty.admin.merchant_settings.save_settings')|replace("'", "\\'") }}'"></span>
</button>
</div>
</form>

View File

@@ -12,7 +12,7 @@
{% block content %}
{% call detail_page_header("isNewProgram ? 'Create Program: ' + (merchant?.name || '') : 'Edit Program: ' + (merchant?.name || '')", '/admin/loyalty/merchants/' ~ merchant_id, subtitle_show='merchant') %}
<span x-text="isNewProgram ? $t('loyalty.admin.program_edit.create_subtitle') : $t('loyalty.admin.program_edit.edit_subtitle')"></span>
<span x-text="isNewProgram ? '{{ _('loyalty.admin.program_edit.create_subtitle')|replace("'", "\\'") }}' : '{{ _('loyalty.admin.program_edit.edit_subtitle')|replace("'", "\\'") }}'"></span>
{% endcall %}
{{ loading_state(_('loyalty.admin.program_edit.loading')) }}

View File

@@ -139,7 +139,7 @@
<div class="flex flex-col items-center">
<span x-html="$icon('gift', 'w-12 h-12 mb-2 text-gray-300')"></span>
<p class="font-medium">{{ _('loyalty.admin.programs.no_programs') }}</p>
<p class="text-xs mt-1" x-text="filters.search || filters.is_active ? $t('loyalty.admin.programs.adjust_filters') : $t('loyalty.admin.programs.no_merchants_yet')"></p>
<p class="text-xs mt-1" x-text="filters.search || filters.is_active ? '{{ _('loyalty.admin.programs.adjust_filters')|replace("'", "\\'") }}' : '{{ _('loyalty.admin.programs.no_merchants_yet')|replace("'", "\\'") }}'"></p>
</div>
</td>
</tr>
@@ -177,7 +177,7 @@
<span x-text="program.loyalty_type?.charAt(0).toUpperCase() + program.loyalty_type?.slice(1) || 'Unknown'"></span>
</span>
<p class="text-xs text-gray-500 mt-1" x-show="program.is_points_enabled">
<span x-text="program.points_per_euro"></span> <span x-text="$t('loyalty.admin.programs.pt_per_eur')"></span>
<span x-text="program.points_per_euro"></span> {{ _('loyalty.admin.programs.pt_per_eur') }}
</p>
</td>
@@ -201,7 +201,7 @@
<td class="px-4 py-3 text-xs">
<span class="inline-flex items-center px-2 py-1 font-semibold leading-tight rounded-full"
:class="program.is_active ? 'text-green-700 bg-green-100 dark:bg-green-700 dark:text-green-100' : 'text-gray-700 bg-gray-100 dark:text-gray-100 dark:bg-gray-700'">
<span x-text="program.is_active ? $t('loyalty.common.active') : $t('loyalty.common.inactive')"></span>
<span x-text="program.is_active ? '{{ _('loyalty.common.active')|replace("'", "\\'") }}' : '{{ _('loyalty.common.inactive')|replace("'", "\\'") }}'"></span>
</span>
</td>

View File

@@ -305,7 +305,7 @@
<button type="submit" :disabled="saving"
class="flex items-center px-6 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50">
<span x-show="saving" x-html="$icon('spinner', 'w-4 h-4 mr-2 animate-spin')"></span>
<span x-text="saving ? $t('loyalty.common.saving') : (isNewProgram ? $t('loyalty.shared.program_form.create_program') : $t('loyalty.shared.program_form.save_changes'))"></span>
<span x-text="saving ? '{{ _('loyalty.common.saving')|replace("'", "\\'") }}' : (isNewProgram ? '{{ _('loyalty.shared.program_form.create_program')|replace("'", "\\'") }}' : '{{ _('loyalty.shared.program_form.save_changes')|replace("'", "\\'") }}')"></span>
</button>
</div>
</div>

View File

@@ -163,7 +163,7 @@
<p class="text-xs text-gray-500 dark:text-gray-400 mb-1">{{ _('loyalty.shared.program_view.staff_pin_required') }}</p>
<span class="inline-flex items-center px-2 py-1 text-xs font-semibold leading-tight rounded-full"
:class="program?.require_staff_pin ? 'text-green-700 bg-green-100 dark:bg-green-700 dark:text-green-100' : 'text-gray-700 bg-gray-100 dark:bg-gray-700 dark:text-gray-100'">
<span x-text="program?.require_staff_pin ? $t('loyalty.common.yes') : $t('loyalty.common.no')"></span>
<span x-text="program?.require_staff_pin ? '{{ _('loyalty.common.yes')|replace("'", "\\'") }}' : '{{ _('loyalty.common.no')|replace("'", "\\'") }}'"></span>
</span>
</div>
</div>

View File

@@ -122,8 +122,8 @@
<td colspan="6" class="px-4 py-8 text-center text-gray-600 dark:text-gray-400">
<div class="flex flex-col items-center">
<span x-html="$icon('users', 'w-12 h-12 mb-2 text-gray-300')"></span>
<p class="font-medium" x-text="$t('loyalty.store.cards.no_members_found')"></p>
<p class="text-xs mt-1" x-text="filters.search ? $t('loyalty.store.cards.try_adjusting_search') : $t('loyalty.store.cards.enroll_first_customer')"></p>
<p class="font-medium">{{ _('loyalty.store.cards.no_members') }}</p>
<p class="text-xs mt-1" x-text="filters.search ? '{{ _('loyalty.store.cards.adjust_search')|replace("'", "\\'") }}' : '{{ _('loyalty.store.cards.enroll_first')|replace("'", "\\'") }}'"></p>
</div>
</td>
</tr>
@@ -150,13 +150,12 @@
<td class="px-4 py-3 text-xs">
<span class="px-2 py-1 font-semibold leading-tight rounded-full"
:class="card.is_active ? 'text-green-700 bg-green-100 dark:bg-green-700 dark:text-green-100' : 'text-gray-700 bg-gray-100 dark:bg-gray-700 dark:text-gray-100'"
x-text="card.is_active ? $t('loyalty.common.active') : $t('loyalty.common.inactive')"></span>
x-text="card.is_active ? '{{ _('loyalty.common.active')|replace("'", "\\'") }}' : '{{ _('loyalty.common.inactive')|replace("'", "\\'") }}'"></span>
</td>
<td class="px-4 py-3">
<a :href="'/store/{{ store_code }}/loyalty/cards/' + card.id"
class="text-purple-600 hover:text-purple-700 dark:text-purple-400"
x-text="$t('loyalty.common.view')">
</a>
>{{ _('loyalty.common.view') }}</a>
</td>
</tr>
</template>

View File

@@ -108,7 +108,7 @@
<button type="submit" :disabled="enrolling || !form.first_name || !form.email"
class="flex items-center px-6 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50">
<span x-show="enrolling" x-html="$icon('spinner', 'w-4 h-4 mr-2 animate-spin')"></span>
<span x-text="enrolling ? $t('loyalty.store.enroll.enrolling') : $t('loyalty.store.enroll.enroll_customer')"></span>
<span x-text="enrolling ? '{{ _('loyalty.store.enroll.enrolling')|replace("'", "\\'") }}' : '{{ _('loyalty.store.enroll.enroll_customer')|replace("'", "\\'") }}'"></span>
</button>
</div>
</form>

View File

@@ -82,7 +82,7 @@
class="w-full flex items-center justify-center px-4 py-3 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 focus:outline-none disabled:opacity-50"
>
<span x-show="lookingUp" x-html="$icon('spinner', 'w-5 h-5 mr-2 animate-spin')"></span>
<span x-text="lookingUp ? $t('loyalty.store.terminal.looking_up') : $t('loyalty.store.terminal.look_up_customer')"></span>
<span x-text="lookingUp ? '{{ _('loyalty.store.terminal.looking_up')|replace("'", "\\'") }}' : '{{ _('loyalty.store.terminal.look_up_customer')|replace("'", "\\'") }}'"></span>
</button>
<!-- Divider -->
@@ -137,7 +137,7 @@
<!-- Points balance (for points and hybrid) -->
<template x-if="program?.is_points_enabled">
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400" x-text="$t('loyalty.store.terminal.points_balance')"></p>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">{{ _('loyalty.store.terminal.points_balance') }}</p>
<p class="text-3xl font-bold"
:style="'color: ' + (program?.card_color || '#4F46E5')"
x-text="formatNumber(selectedCard?.points_balance || 0)"></p>
@@ -146,7 +146,7 @@
<!-- Stamps progress (for stamps and hybrid) -->
<template x-if="program?.is_stamps_enabled">
<div :class="program?.is_points_enabled ? 'mt-3 pt-3 border-t border-gray-200 dark:border-gray-700' : ''">
<p class="text-sm font-medium text-gray-600 dark:text-gray-400" x-text="$t('loyalty.store.terminal.stamps')"></p>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">{{ _('loyalty.store.terminal.stamps') }}</p>
<p class="text-3xl font-bold"
:style="'color: ' + (program?.card_color || '#4F46E5')"
x-text="(selectedCard?.stamp_count || 0) + ' / ' + (program?.stamps_target || 10)"></p>
@@ -175,7 +175,7 @@
{{ _('loyalty.store.terminal.add_stamp') }}
</button>
<template x-if="!selectedCard?.can_stamp && selectedCard?.cooldown_ends_at">
<p class="text-xs text-red-500 mt-2" x-text="$t('loyalty.store.terminal.cooldown_active')"></p>
<p class="text-xs text-red-500 mt-2">{{ _('loyalty.store.terminal.cooldown_active') }}</p>
</template>
</div>
</template>
@@ -357,7 +357,7 @@
:disabled="pinDigits.length !== 4 || processing"
class="px-4 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50">
<span x-show="processing" x-html="$icon('spinner', 'w-4 h-4 mr-2 inline animate-spin')"></span>
<span x-text="processing ? $t('loyalty.store.terminal.processing') : $t('loyalty.store.terminal.confirm')"></span>
<span x-text="processing ? '{{ _('loyalty.store.terminal.processing')|replace("'", "\\'") }}' : '{{ _('loyalty.store.terminal.confirm')|replace("'", "\\'") }}'"></span>
</button>
</div>
</div>

View File

@@ -35,7 +35,16 @@
"failed_to_delete_notification": "Failed to delete notification",
"failed_to_load_alerts": "Failed to load alerts",
"alert_resolved_successfully": "Alert resolved successfully",
"failed_to_resolve_alert": "Failed to resolve alert"
"failed_to_resolve_alert": "Failed to resolve alert",
"no_template_for_language": "Keine Vorlage für {language} - erstellen Sie eine durch Speichern",
"failed_to_save_template": "Vorlage konnte nicht gespeichert werden",
"test_email_sent": "Test-E-Mail an {email} gesendet"
},
"confirmations": {
"delete_notification": "Sind Sie sicher, dass Sie diese Benachrichtigung löschen möchten?",
"close_conversation": "Diese Konversation schließen?",
"close_conversation_admin": "Sind Sie sicher, dass Sie diese Konversation schließen möchten?",
"delete_customization": "Sind Sie sicher, dass Sie Ihre Anpassung löschen und zur Plattform-Standardvorlage zurückkehren möchten?"
},
"features": {
"messaging_basic": {
@@ -78,6 +87,7 @@
"reply_to": "Antwort an",
"related_entity": "Verknüpfte Entität",
"error_message": "Fehlermeldung",
"retry_count": "Wiederholungsversuche",
"actions": "Aktionen",
"view_detail": "Details anzeigen",
"email_detail": "E-Mail-Detail",
@@ -85,6 +95,7 @@
"text_preview": "Text-Vorschau",
"metadata": "Metadaten",
"content": "Inhalt",
"status_timeline": "Status-Verlauf",
"filter_by_recipient": "Nach Empfänger-E-Mail suchen...",
"filter_by_status": "Alle Status",
"filter_by_template": "Alle Vorlagen",
@@ -104,7 +115,8 @@
"status_delivered": "Zugestellt",
"status_bounced": "Abgewiesen",
"status_opened": "Geöffnet",
"status_clicked": "Angeklickt"
"status_clicked": "Angeklickt",
"retention_note": "E-Mail-Inhalte werden 90 Tage aufbewahrt. Metadaten werden unbegrenzt gespeichert."
},
"permissions": {
"view_messages": "Nachrichten anzeigen",

View File

@@ -35,7 +35,16 @@
"failed_to_delete_notification": "Failed to delete notification",
"failed_to_load_alerts": "Failed to load alerts",
"alert_resolved_successfully": "Alert resolved successfully",
"failed_to_resolve_alert": "Failed to resolve alert"
"failed_to_resolve_alert": "Failed to resolve alert",
"no_template_for_language": "Pas de modèle pour {language} - créez-en un en enregistrant",
"failed_to_save_template": "Échec de l'enregistrement du modèle",
"test_email_sent": "E-mail de test envoyé à {email}"
},
"confirmations": {
"delete_notification": "Êtes-vous sûr de vouloir supprimer cette notification ?",
"close_conversation": "Fermer cette conversation ?",
"close_conversation_admin": "Êtes-vous sûr de vouloir fermer cette conversation ?",
"delete_customization": "Êtes-vous sûr de vouloir supprimer votre personnalisation et revenir au modèle par défaut de la plateforme ?"
},
"features": {
"messaging_basic": {
@@ -78,6 +87,7 @@
"reply_to": "Répondre à",
"related_entity": "Entité liée",
"error_message": "Message d'erreur",
"retry_count": "Tentatives",
"actions": "Actions",
"view_detail": "Voir le détail",
"email_detail": "Détail de l'e-mail",
@@ -85,6 +95,7 @@
"text_preview": "Aperçu texte",
"metadata": "Métadonnées",
"content": "Contenu",
"status_timeline": "Chronologie du statut",
"filter_by_recipient": "Rechercher par e-mail du destinataire...",
"filter_by_status": "Tous les statuts",
"filter_by_template": "Tous les modèles",
@@ -104,7 +115,8 @@
"status_delivered": "Livré",
"status_bounced": "Rebondi",
"status_opened": "Ouvert",
"status_clicked": "Cliqué"
"status_clicked": "Cliqué",
"retention_note": "Le contenu des e-mails est conservé pendant 90 jours. Les métadonnées sont conservées indéfiniment."
},
"permissions": {
"view_messages": "Voir les messages",

View File

@@ -35,7 +35,16 @@
"failed_to_delete_notification": "Failed to delete notification",
"failed_to_load_alerts": "Failed to load alerts",
"alert_resolved_successfully": "Alert resolved successfully",
"failed_to_resolve_alert": "Failed to resolve alert"
"failed_to_resolve_alert": "Failed to resolve alert",
"no_template_for_language": "Keng Virlag fir {language} - erstellt eng andeems Dir späichert",
"failed_to_save_template": "Virlag konnt net gespäichert ginn",
"test_email_sent": "Test-E-Mail u {email} geschéckt"
},
"confirmations": {
"delete_notification": "Sidd Dir sécher datt Dir dës Notifikatioun läsche wëllt?",
"close_conversation": "Dës Conversatioun zoumaachen?",
"close_conversation_admin": "Sidd Dir sécher datt Dir dës Conversatioun zoumaache wëllt?",
"delete_customization": "Sidd Dir sécher datt Dir Är Personnaliséierung läschen an zum Plattform-Standard zréckgoe wëllt?"
},
"features": {
"messaging_basic": {
@@ -59,7 +68,55 @@
"account_settings": "Kont-Astellungen",
"messages": "Messagen",
"notifications": "Notifikatiounen",
"email_templates": "E-Mail-Virlagen"
"email_templates": "E-Mail-Virlagen",
"email_logs": "E-Mail-Protokoller"
},
"email_logs": {
"title": "E-Mail-Protokoller",
"subtitle": "All E-Mailen iwwerpréiwen déi iwwer d'Plattform geschéckt goufen",
"recipient": "Empfänger",
"subject": "Sujet",
"template": "Virlag",
"status": "Status",
"store": "Buttek",
"date": "Datum",
"sent_at": "Geschéckt um",
"provider": "Ubidder",
"from": "Vun",
"to": "Un",
"reply_to": "Äntwert un",
"related_entity": "Verbonnen Entitéit",
"error_message": "Feelernotiz",
"retry_count": "Widderholungsversich",
"actions": "Aktiounen",
"view_detail": "Detailer kucken",
"email_detail": "E-Mail-Detail",
"html_preview": "HTML-Virschau",
"text_preview": "Text-Virschau",
"metadata": "Metadaten",
"content": "Inhalt",
"status_timeline": "Status-Verlaf",
"filter_by_recipient": "No Empfänger-E-Mail sichen...",
"filter_by_status": "All Statussen",
"filter_by_template": "All Virlagen",
"filter_by_store": "All Butteker",
"date_from": "Vun Datum",
"date_to": "Bis Datum",
"apply_filters": "Uwenden",
"reset_filters": "Zrécksetzen",
"total_sent": "Total geschéckt",
"total_failed": "Feelgeschloen",
"total_pending": "Aussteesend",
"total_delivered": "Zougestallt",
"no_logs": "Keng E-Mail-Protokoller fonnt",
"status_sent": "Geschéckt",
"status_failed": "Feelgeschloen",
"status_pending": "Aussteesend",
"status_delivered": "Zougestallt",
"status_bounced": "Zréckgeschéckt",
"status_opened": "Opgemaach",
"status_clicked": "Geklickt",
"retention_note": "E-Mail-Inhalt gëtt 90 Deeg gespäichert. Metadaten ginn onbegrenzt gehalen."
},
"permissions": {
"view_messages": "Messagen kucken",

View File

@@ -90,6 +90,13 @@
"failed_to_load_user": "Failed to load user",
"user_updated_successfully": "User updated successfully"
},
"confirmations": {
"enable_all_modules": "Alle Module werden aktiviert. Fortfahren?",
"disable_optional_modules": "Alle optionalen Module werden deaktiviert, nur Kernmodule bleiben aktiv. Fortfahren?",
"reset_theme": "Theme auf Standard zurücksetzen? Dies kann nicht rückgängig gemacht werden.",
"show_all_menu_items": "Alle Menüpunkte werden angezeigt. Fortfahren?",
"hide_all_menu_items": "Alle Menüpunkte werden ausgeblendet (außer obligatorische). Sie können dann die gewünschten aktivieren. Fortfahren?"
},
"features": {
"team_members": {
"name": "Teammitglieder",

View File

@@ -90,6 +90,13 @@
"failed_to_load_user": "Failed to load user",
"user_updated_successfully": "User updated successfully"
},
"confirmations": {
"enable_all_modules": "Ceci activera tous les modules. Continuer ?",
"disable_optional_modules": "Ceci désactivera tous les modules optionnels, ne conservant que les modules principaux. Continuer ?",
"reset_theme": "Réinitialiser le thème par défaut ? Cette action est irréversible.",
"show_all_menu_items": "Ceci affichera tous les éléments de menu. Continuer ?",
"hide_all_menu_items": "Ceci masquera tous les éléments de menu (sauf les obligatoires). Vous pourrez ensuite activer ceux que vous souhaitez. Continuer ?"
},
"features": {
"team_members": {
"name": "Membres de l'équipe",

View File

@@ -90,6 +90,13 @@
"failed_to_load_user": "Failed to load user",
"user_updated_successfully": "User updated successfully"
},
"confirmations": {
"enable_all_modules": "All Moduler ginn aktivéiert. Weidermaachen?",
"disable_optional_modules": "All optional Moduler ginn deaktivéiert, nëmmen Kärmoduler bleiwen aktiv. Weidermaachen?",
"reset_theme": "Theme op Standard zrécksetzen? Dëst kann net réckgängeg gemaach ginn.",
"show_all_menu_items": "All Menüpunkten ginn ugewisen. Weidermaachen?",
"hide_all_menu_items": "All Menüpunkten ginn verstopp (ausser obligatoresch). Dir kënnt dann déi gewënscht aktivéieren. Weidermaachen?"
},
"features": {
"team_members": {
"name": "Team-Memberen",

View File

@@ -220,6 +220,10 @@
"datetime": "DD.MM.YYYY HH:mm",
"currency": "{amount} {symbol}"
},
"clipboard": {
"copied": "In die Zwischenablage kopiert",
"failed": "Kopieren fehlgeschlagen"
},
"onboarding": {
"banner": {
"title": "Erste Schritte",

View File

@@ -220,6 +220,10 @@
"datetime": "DD/MM/YYYY HH:mm",
"currency": "{amount} {symbol}"
},
"clipboard": {
"copied": "Copié dans le presse-papiers",
"failed": "Échec de la copie"
},
"onboarding": {
"banner": {
"title": "Premiers pas",

View File

@@ -220,6 +220,10 @@
"datetime": "DD.MM.YYYY HH:mm",
"currency": "{amount} {symbol}"
},
"clipboard": {
"copied": "An d'Zwëschenoflag kopéiert",
"failed": "Kopéiere feelgeschloen"
},
"onboarding": {
"banner": {
"title": "Ufänken",