Files
ackify-ce/docs/fr/features/i18n.md
Benjamin 68426bc882 feat: add PKCE support to OAuth2 flow for enhanced security
- Implement PKCE (Proof Key for Code Exchange) with S256 method
- Add crypto/pkce module with code verifier and challenge generation
- Modify OAuth flow to include code_challenge in authorization requests
- Update HandleCallback to validate code_verifier during token exchange
- Extend session lifetime from 7 to 30 days
- Add comprehensive unit tests for PKCE functions
- Maintain backward compatibility with fallback for non-PKCE sessions
- Add detailed logging for OAuth flow with PKCE tracking

PKCE enhances security by preventing authorization code interception
attacks, as recommended by OAuth 2.1 and OIDC standards.

feat: add encrypted refresh token storage with automatic cleanup

- Add oauth_sessions table for storing encrypted refresh tokens
- Implement AES-256-GCM encryption for refresh tokens using cookie secret
- Create OAuth session repository with full CRUD operations
- Add SessionWorker for automatic cleanup of expired sessions
- Configure cleanup to run every 24h for sessions older than 37 days
- Modify OAuth flow to store refresh tokens after successful authentication
- Track client IP and user agent for session security validation
- Link OAuth sessions to user sessions via session ID
- Add comprehensive encryption tests with security validations
- Integrate SessionWorker into server lifecycle with graceful shutdown

This enables persistent OAuth sessions with secure token storage,
reducing the need for frequent re-authentication from 7 to 30 days.
2025-10-26 02:32:10 +02:00

5.6 KiB

Internationalisation (i18n)

Support multilingue complet du frontend Ackify.

Langues Supportées

  • 🇫🇷 Français (par défaut)
  • 🇬🇧 English (fallback)
  • 🇪🇸 Español
  • 🇩🇪 Deutsch
  • 🇮🇹 Italiano

Frontend (Vue.js)

Sélection de Langue

Le frontend détecte automatiquement la langue via :

  1. localStorage - Choix utilisateur sauvegardé
  2. navigator.language - Langue du navigateur
  3. Fallback - English si non supportée

Switcher de Langue

Interface utilisateur avec drapeaux Unicode :

🇫🇷 FR | 🇬🇧 EN | 🇪🇸 ES | 🇩🇪 DE | 🇮🇹 IT

Clic → Langue change + sauvegarde dans localStorage

Fichiers de Traduction

Localisés dans /webapp/src/locales/ :

locales/
├── fr.json    # Français
├── en.json    # English
├── es.json    # Español
├── de.json    # Deutsch
└── it.json    # Italiano

Structure JSON

{
  "home": {
    "title": "Ackify - Proof of Read",
    "subtitle": "Signatures cryptographiques de lecture"
  },
  "document": {
    "sign": "Signer ce document",
    "signed": "Document signé",
    "signatures": "{count} confirmation | {count} confirmations"
  }
}

Pluralisation :

"signatures": "{count} confirmation | {count} confirmations"

Usage :

{{ $t('document.signatures', { count: 42 }) }}
// → "42 confirmations"

Backend (Go)

Templates Email

Les emails utilisent des templates multilingues dans /backend/templates/emails/ :

templates/emails/
├── fr/
│   ├── reminder.html
│   └── reminder.txt
├── en/
│   ├── reminder.html
│   └── reminder.txt
├── es/...
├── de/...
└── it/...

Envoi avec Locale

POST /api/v1/admin/documents/doc_id/reminders
Content-Type: application/json

{
  "emails": ["user@company.com"],
  "locale": "fr"
}

Le backend charge le template fr/reminder.html.

Configuration

# Langue par défaut pour les emails (défaut: en)
ACKIFY_MAIL_DEFAULT_LOCALE=fr

Ajouter une Langue

Frontend

  1. Créer le fichier de traduction :
cd webapp/src/locales
cp en.json pt.json  # Portugais
  1. Traduire :
{
  "home": {
    "title": "Ackify - Prova de Leitura",
    "subtitle": "Assinaturas criptográficas de leitura"
  }
}
  1. Enregistrer dans i18n :
// webapp/src/i18n.ts
import pt from './locales/pt.json'

const i18n = createI18n({
  locale: 'fr',
  fallbackLocale: 'en',
  messages: {
    fr, en, es, de, it,
    pt  // Ajouter ici
  }
})
  1. Ajouter au sélecteur :
<!-- components/LanguageSwitcher.vue -->
<button @click="changeLocale('pt')">🇵🇹 PT</button>

Backend

  1. Créer le répertoire :
mkdir -p backend/templates/emails/pt
  1. Créer les templates :
cp backend/templates/emails/en/reminder.html backend/templates/emails/pt/
cp backend/templates/emails/en/reminder.txt backend/templates/emails/pt/
  1. Traduire les templates

  2. Rebuild :

docker compose up -d --force-recreate ackify-ce --build

Vérification i18n

Script de Validation

Le projet inclut un script pour vérifier la complétude des traductions :

cd webapp
npm run lint:i18n

Output :

✅ fr.json - 156 keys
✅ en.json - 156 keys
✅ es.json - 156 keys
✅ de.json - 156 keys
✅ it.json - 156 keys

All translations are complete!

CI/CD

Le script s'exécute automatiquement dans GitHub Actions pour bloquer les PRs avec traductions manquantes.

Bonnes Pratiques

Clés de Traduction

  • Utiliser des clés structurées : feature.action.label
  • Grouper par page/composant
  • Éviter les clés trop génériques (button, title)
  • Utiliser des placeholders : {count}, {name}

Exemple :

{
  "admin": {
    "documents": {
      "list": {
        "title": "Liste des documents",
        "count": "{count} document | {count} documents"
      }
    }
  }
}

Synchronisation

Lors de l'ajout de nouvelles clés en français :

# Sync script (à créer)
node scripts/sync-i18n-from-fr.js

Copie automatiquement les nouvelles clés vers les autres langues avec [TODO].

Textes Longs

Pour les textes longs, utiliser des arrays :

{
  "help": {
    "intro": [
      "Ackify permet de créer des signatures cryptographiques.",
      "Chaque signature est horodatée et non-répudiable.",
      "Les données sont stockées de manière immuable."
    ]
  }
}

Usage :

<p v-for="line in $tm('help.intro')" :key="line">
  {{ line }}
</p>

Formats Spécifiques

Dates

// Formater avec la locale courante
import { useI18n } from 'vue-i18n'

const { locale } = useI18n()
const formatted = new Date().toLocaleDateString(locale.value, {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
})
// fr: "15 janvier 2025"
// en: "January 15, 2025"

Nombres

const formatted = (42000).toLocaleString(locale.value)
// fr: "42 000"
// en: "42,000"

SEO & Meta Tags

Les meta tags sont traduits dynamiquement :

<script setup>
import { useI18n } from 'vue-i18n'
import { useHead } from '@vueuse/head'

const { t } = useI18n()

useHead({
  title: t('home.title'),
  meta: [
    { name: 'description', content: t('home.description') }
  ]
})
</script>

Documentation Complète

Pour plus de détails sur l'implémentation i18n du frontend, voir :

webapp/I18N.md

Ce fichier contient :

  • Architecture complète vue-i18n
  • Guide de contribution
  • Scripts de synchronisation
  • Exemples avancés