Files
ackify/docs/en/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.3 KiB

Internationalization (i18n)

Complete multilingual support for Ackify frontend.

Supported Languages

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

Frontend (Vue.js)

Language Selection

The frontend automatically detects language via:

  1. localStorage - Saved user choice
  2. navigator.language - Browser language
  3. Fallback - English if not supported

Language Switcher

User interface with Unicode flags:

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

Click → Language changes + saves to localStorage

Translation Files

Located in /webapp/src/locales/:

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

JSON Structure

{
  "home": {
    "title": "Ackify - Proof of Read",
    "subtitle": "Cryptographic read signatures"
  },
  "document": {
    "sign": "Sign this document",
    "signed": "Document signed",
    "signatures": "{count} confirmation | {count} confirmations"
  }
}

Pluralization:

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

Usage:

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

Backend (Go)

Email Templates

Emails use multilingual templates in /backend/templates/emails/:

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

Sending with Locale

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

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

Backend loads template fr/reminder.html.

Configuration

# Default language for emails (default: en)
ACKIFY_MAIL_DEFAULT_LOCALE=fr

Adding a Language

Frontend

  1. Create translation file:
cd webapp/src/locales
cp en.json pt.json  # Portuguese
  1. Translate:
{
  "home": {
    "title": "Ackify - Prova de Leitura",
    "subtitle": "Assinaturas criptográficas de leitura"
  }
}
  1. Register in 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  // Add here
  }
})
  1. Add to selector:
<!-- components/LanguageSwitcher.vue -->
<button @click="changeLocale('pt')">🇵🇹 PT</button>

Backend

  1. Create directory:
mkdir -p backend/templates/emails/pt
  1. Create templates:
cp backend/templates/emails/en/reminder.html backend/templates/emails/pt/
cp backend/templates/emails/en/reminder.txt backend/templates/emails/pt/
  1. Translate templates

  2. Rebuild:

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

i18n Verification

Validation Script

Project includes a script to verify translation completeness:

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

Script automatically runs in GitHub Actions to block PRs with missing translations.

Best Practices

Translation Keys

  • Use structured keys: feature.action.label
  • Group by page/component
  • Avoid too generic keys (button, title)
  • Use placeholders: {count}, {name}

Example:

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

Synchronization

When adding new keys in French:

# Sync script (to create)
node scripts/sync-i18n-from-fr.js

Automatically copies new keys to other languages with [TODO].

Long Texts

For long texts, use arrays:

{
  "help": {
    "intro": [
      "Ackify allows creating cryptographic signatures.",
      "Each signature is timestamped and non-repudiable.",
      "Data is stored immutably."
    ]
  }
}

Usage:

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

Specific Formats

Dates

// Format with current locale
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"

Numbers

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

SEO & Meta Tags

Meta tags are dynamically translated:

<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>

Complete Documentation

For more details on frontend i18n implementation, see:

webapp/I18N.md

This file contains:

  • Complete vue-i18n architecture
  • Contribution guide
  • Synchronization scripts
  • Advanced examples