feat(i18n): Complete localization of marketing frontend with react-i18next, prefixed URLs, JSON migrations, and automation
This commit is contained in:
@@ -97,39 +97,36 @@ use Illuminate\\Database\\Migrations\\Migration;
|
||||
use Illuminate\\Database\\Schema\\Blueprint;
|
||||
use Illuminate\\Support\\Facades\\Schema;
|
||||
|
||||
// Event types (global)
|
||||
Schema::create('event_types', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->json('name');
|
||||
$table->json('name'); // Translatable: { "de": "Hochzeit", "en": "Wedding" }
|
||||
$table->string('slug', 100)->unique();
|
||||
$table->string('icon', 64)->nullable();
|
||||
$table->json('settings')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Events (tenant-scoped)
|
||||
Schema::create('events', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
|
||||
$table->json('name');
|
||||
$table->json('name'); // Translatable: { "de": "Event Name", "en": "Event Name" }
|
||||
$table->date('date');
|
||||
$table->string('slug');
|
||||
$table->json('description')->nullable();
|
||||
$table->json('description')->nullable(); // Translatable
|
||||
$table->json('settings')->nullable();
|
||||
$table->foreignId('event_type_id')->constrained('event_types');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->string('default_locale', 5)->default('de');
|
||||
$table->string('default_locale', 5)->default('de'); // For event-specific i18n fallback
|
||||
$table->timestamps();
|
||||
$table->unique(['tenant_id', 'slug']);
|
||||
});
|
||||
|
||||
// Emotions (global library)
|
||||
Schema::create('emotions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->json('name');
|
||||
$table->json('name'); // Translatable: { "de": "Freude", "en": "Joy" }
|
||||
$table->string('icon', 50);
|
||||
$table->string('color', 7);
|
||||
$table->json('description')->nullable();
|
||||
$table->json('description')->nullable(); // Translatable
|
||||
$table->integer('sort_order')->default(0);
|
||||
$table->boolean('is_active')->default(true);
|
||||
});
|
||||
@@ -141,22 +138,21 @@ Schema::create('emotion_event_type', function (Blueprint $table) {
|
||||
$table->primary(['emotion_id', 'event_type_id']);
|
||||
});
|
||||
|
||||
// Tasks (with optional tenant/event scoping)
|
||||
Schema::create('tasks', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('emotion_id')->constrained('emotions');
|
||||
$table->foreignId('event_type_id')->nullable()->constrained('event_types')->nullOnDelete();
|
||||
$table->json('title');
|
||||
$table->json('description');
|
||||
$table->json('title'); // Translatable
|
||||
$table->json('description'); // Translatable
|
||||
$table->string('difficulty', 16)->default('easy'); // app enum
|
||||
$table->json('example_text')->nullable();
|
||||
$table->json('example_text')->nullable(); // Translatable
|
||||
$table->integer('sort_order')->default(0);
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->foreignId('tenant_id')->nullable()->constrained('tenants')->nullOnDelete();
|
||||
$table->string('scope', 16)->default('global'); // global|tenant|event
|
||||
$table->foreignId('event_id')->nullable()->constrained('events')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
$table->unique(['tenant_id', 'emotion_id', 'title']);
|
||||
$table->unique(['tenant_id', 'emotion_id', 'title->de']); // Example for de fallback; adjust for multi-locale
|
||||
});
|
||||
|
||||
// Photos
|
||||
@@ -196,8 +192,8 @@ Schema::create('legal_pages', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('tenant_id')->nullable()->constrained('tenants')->nullOnDelete();
|
||||
$table->string('slug', 32); // imprint|privacy|terms|custom
|
||||
$table->json('title');
|
||||
$table->json('body_markdown');
|
||||
$table->json('title'); // Translatable
|
||||
$table->json('body_markdown'); // Translatable Markdown content per locale
|
||||
$table->string('locale_fallback', 5)->default('de');
|
||||
$table->unsignedInteger('version')->default(1);
|
||||
$table->timestamp('effective_from')->nullable();
|
||||
@@ -211,3 +207,4 @@ Schema::create('legal_pages', function (Blueprint $table) {
|
||||
- Prefer app-level enums (string columns + validation) over DB `ENUM`.
|
||||
- Use `cascadeOnDelete()` only where child data must be removed with parent; otherwise `nullOnDelete()`.
|
||||
- Every tenant-owned table should include `tenant_id` and appropriate composite indexes.
|
||||
- **i18n Integration**: JSON fields (e.g., `name`, `description`) store locale-specific values as `{ "de": "Text", "en": "Text" }`. Use Laravel's `json` cast or spatie/laravel-translatable for access. Fallback to `default_locale` or global fallback ('de'). Update via Filament resources with locale selectors. Ensure indexes on JSON paths if querying (e.g., `->de` for German titles).
|
||||
|
||||
Reference in New Issue
Block a user