Mehrsprachigkeit der Oberfläche (Deutsch Standard, Englisch wählbar)
- Sprache je Benutzer (users.locale), Middleware SetLocale, Auswahl unter Einstellungen → Darstellung; angebotene Sprachen in config/app.php - Übersetzungen über Laravel Lang (lang/de.json, lang/de/*.php), Seitentitel aus #[Title] werden übersetzt - Eigene Texte und Korrekturen in lang/project/de.json, übernommen mit `artisan lang:apply-project` (überstehen `lang:update`) - Tests: jede im Code verwendete Übersetzung ist vorhanden, eigene Übersetzungen sind übernommen, Sprachwahl und Standardsprache; SecurityTest prüft Texte sprachunabhängig über __() - lang/ von Pint ausgenommen (erzeugte Dateien) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
cdd2d5b676
commit
db2d5d6228
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* Überträgt die eigenen Übersetzungen aus lang/project/{locale}.json in lang/{locale}.json.
|
||||
*
|
||||
* lang/{locale}.json wird von Laravel Lang erzeugt; `lang:update` setzt dabei geänderte Paket-Texte
|
||||
* zurück. Eigene Texte und Korrekturen stehen deshalb in lang/project/ und haben Vorrang.
|
||||
*/
|
||||
class ApplyProjectTranslations extends Command
|
||||
{
|
||||
protected $signature = 'lang:apply-project';
|
||||
|
||||
protected $description = 'Eigene Übersetzungen aus lang/project/ in lang/{locale}.json übernehmen';
|
||||
|
||||
public function handle(Filesystem $files): int
|
||||
{
|
||||
foreach ($files->glob(lang_path('project/*.json')) as $projectFile) {
|
||||
$locale = pathinfo($projectFile, PATHINFO_FILENAME);
|
||||
$target = lang_path("{$locale}.json");
|
||||
|
||||
$translations = $files->exists($target)
|
||||
? json_decode($files->get($target), true, flags: JSON_THROW_ON_ERROR)
|
||||
: [];
|
||||
$project = json_decode($files->get($projectFile), true, flags: JSON_THROW_ON_ERROR);
|
||||
|
||||
$files->put($target, json_encode(
|
||||
array_merge($translations, $project),
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR,
|
||||
).PHP_EOL);
|
||||
|
||||
$this->components->info(count($project)." Übersetzungen nach lang/{$locale}.json übernommen.");
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Setzt die Sprache der Oberfläche: die des angemeldeten Benutzers, sonst die Standardsprache.
|
||||
*/
|
||||
class SetLocale
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
/** @var array<string, string> $available */
|
||||
$available = config('app.available_locales');
|
||||
$locale = $request->user()?->locale;
|
||||
|
||||
if (! is_string($locale) || ! array_key_exists($locale, $available)) {
|
||||
$locale = config('app.locale');
|
||||
}
|
||||
|
||||
App::setLocale($locale);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,36 @@
|
||||
|
||||
namespace App\Livewire\Settings;
|
||||
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Appearance settings')]
|
||||
class Appearance extends Component
|
||||
{
|
||||
//
|
||||
public string $locale = '';
|
||||
|
||||
/**
|
||||
* Mount the component.
|
||||
*/
|
||||
public function mount(): void
|
||||
{
|
||||
$this->locale = App::getLocale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the selected interface language and reload the page in that language.
|
||||
*/
|
||||
public function updatedLocale(): void
|
||||
{
|
||||
$this->validate([
|
||||
'locale' => ['required', 'string', Rule::in(array_keys(config('app.available_locales')))],
|
||||
]);
|
||||
|
||||
Auth::user()->update(['locale' => $this->locale]);
|
||||
|
||||
$this->redirectRoute('appearance.edit');
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ use Spatie\Permission\Traits\HasRoles;
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $email
|
||||
* @property string|null $locale
|
||||
* @property Carbon|null $email_verified_at
|
||||
* @property string $password
|
||||
* @property string|null $two_factor_secret
|
||||
@@ -29,7 +30,7 @@ use Spatie\Permission\Traits\HasRoles;
|
||||
* @property Carbon|null $created_at
|
||||
* @property Carbon|null $updated_at
|
||||
*/
|
||||
#[Fillable(['name', 'email', 'password'])]
|
||||
#[Fillable(['name', 'email', 'password', 'locale'])]
|
||||
#[Hidden(['password', 'two_factor_secret', 'two_factor_recovery_codes', 'remember_token'])]
|
||||
class User extends Authenticatable implements MustVerifyEmail, PasskeyUser
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user