From db2d5d62285233206b73a9aac4d0b57dd1d15c68 Mon Sep 17 00:00:00 2001 From: Christoph Date: Thu, 1 Oct 2026 09:52:20 +0200 Subject: [PATCH] =?UTF-8?q?Mehrsprachigkeit=20der=20Oberfl=C3=A4che=20(Deu?= =?UTF-8?q?tsch=20Standard,=20Englisch=20w=C3=A4hlbar)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- CLAUDE.md | 8 + .../Commands/ApplyProjectTranslations.php | 41 + app/Http/Middleware/SetLocale.php | 29 + app/Livewire/Settings/Appearance.php | 27 +- app/Models/User.php | 3 +- bootstrap/app.php | 3 +- composer.json | 1 + composer.lock | 1860 ++++++++++++++++- config/app.php | 13 +- ...10_01_074749_add_locale_to_users_table.php | 29 + docs/tech-stack.md | 3 +- lang/de.json | 426 ++++ lang/de/actions.php | 119 ++ lang/de/auth.php | 9 + lang/de/http-statuses.php | 84 + lang/de/pagination.php | 8 + lang/de/passwords.php | 11 + lang/de/validation.php | 290 +++ lang/en.json | 424 ++++ lang/en/actions.php | 119 ++ lang/en/auth.php | 9 + lang/en/http-statuses.php | 84 + lang/en/pagination.php | 8 + lang/en/passwords.php | 11 + lang/en/validation.php | 290 +++ lang/project/de.json | 18 + pint.json | 5 +- .../livewire/settings/appearance.blade.php | 18 +- resources/views/partials/head.blade.php | 2 +- tests/Feature/LocaleTest.php | 82 + tests/Feature/Settings/SecurityTest.php | 16 +- tests/Unit/TranslationCoverageTest.php | 76 + 32 files changed, 4108 insertions(+), 18 deletions(-) create mode 100644 app/Console/Commands/ApplyProjectTranslations.php create mode 100644 app/Http/Middleware/SetLocale.php create mode 100644 database/migrations/2026_10_01_074749_add_locale_to_users_table.php create mode 100644 lang/de.json create mode 100644 lang/de/actions.php create mode 100644 lang/de/auth.php create mode 100644 lang/de/http-statuses.php create mode 100644 lang/de/pagination.php create mode 100644 lang/de/passwords.php create mode 100644 lang/de/validation.php create mode 100644 lang/en.json create mode 100644 lang/en/actions.php create mode 100644 lang/en/auth.php create mode 100644 lang/en/http-statuses.php create mode 100644 lang/en/pagination.php create mode 100644 lang/en/passwords.php create mode 100644 lang/en/validation.php create mode 100644 lang/project/de.json create mode 100644 tests/Feature/LocaleTest.php create mode 100644 tests/Unit/TranslationCoverageTest.php diff --git a/CLAUDE.md b/CLAUDE.md index 6ac7aca..41f5662 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,6 +56,14 @@ wird nicht umgangen. Begriffe nach Laravel-Konvention englisch (`Controller`, `Policy`, `Job`). - **Länder DE/AT:** gemeinsamer Kern, länderspezifische Logik hinter Schnittstellen in `app/Laender/DE` bzw. `app/Laender/AT`. Fachliche Tabellen bekommen `mandant_id` und `land`. +- **Mehrsprachigkeit:** Jeder sichtbare Text über `__('English source text')`, nie fest im + Template. Standardsprache Deutsch, angebotene Sprachen in `config('app.available_locales')`, + Sprache je Benutzer in `users.locale` (Middleware `SetLocale`). + - `lang/de.json` und `lang/de/*.php` erzeugt Laravel Lang – nicht von Hand ändern. + - Eigene Texte und Korrekturen gehören nach `lang/project/{locale}.json`, danach + `sail artisan lang:apply-project`. Nach `sail artisan lang:update` ebenfalls `lang:apply-project`. + - `TranslationCoverageTest` schlägt fehl, wenn ein Text ohne Übersetzung ist. + - In Tests Texte über `__()` prüfen statt fest auf Englisch oder Deutsch. - **Tests:** Pest als Runner, Tests im PHPUnit-Klassenstil (`tests/Feature`, `tests/Unit`). Tests laufen gegen die Datenbank `testing`, nie gegen `ki_bauin`. - **Commit-Nachrichten** auf Deutsch. diff --git a/app/Console/Commands/ApplyProjectTranslations.php b/app/Console/Commands/ApplyProjectTranslations.php new file mode 100644 index 0000000..5f0ce0a --- /dev/null +++ b/app/Console/Commands/ApplyProjectTranslations.php @@ -0,0 +1,41 @@ +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; + } +} diff --git a/app/Http/Middleware/SetLocale.php b/app/Http/Middleware/SetLocale.php new file mode 100644 index 0000000..46ba5df --- /dev/null +++ b/app/Http/Middleware/SetLocale.php @@ -0,0 +1,29 @@ + $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); + } +} diff --git a/app/Livewire/Settings/Appearance.php b/app/Livewire/Settings/Appearance.php index 7550e32..5f725c7 100644 --- a/app/Livewire/Settings/Appearance.php +++ b/app/Livewire/Settings/Appearance.php @@ -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'); + } } diff --git a/app/Models/User.php b/app/Models/User.php index 0fe2939..ee71f9c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -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 { diff --git a/bootstrap/app.php b/bootstrap/app.php index 1085719..54e7094 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,5 +1,6 @@ withMiddleware(function (Middleware $middleware): void { - // + $middleware->web(append: SetLocale::class); }) ->withExceptions(function (Exceptions $exceptions): void { $exceptions->shouldRenderJsonWhen( diff --git a/composer.json b/composer.json index f89d18a..449a4c4 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "require-dev": { "fakerphp/faker": "^1.24", "larastan/larastan": "^3.9", + "laravel-lang/common": "^6.8", "laravel/pail": "^1.2.5", "laravel/pao": "^1.0.6", "laravel/pint": "^1.27", diff --git a/composer.lock b/composer.lock index 5223b03..6bdb428 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "57105dffaaadf223d295f7e9d25f68f2", + "content-hash": "cb480a37f334d3bd85a5b9b3d8960098", "packages": [ { "name": "bacon/bacon-qr-code", @@ -8061,6 +8061,59 @@ } ], "packages-dev": [ + { + "name": "archtechx/enums", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/archtechx/enums.git", + "reference": "81375b71c176f680880a95e7448d84258cfb5c72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/archtechx/enums/zipball/81375b71c176f680880a95e7448d84258cfb5c72", + "reference": "81375b71c176f680880a95e7448d84258cfb5c72", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "larastan/larastan": "^2.4", + "orchestra/testbench": "^8.0 || ^9.0", + "pestphp/pest": "^2.0", + "pestphp/pest-plugin-laravel": "^2.0" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "ArchTech\\Enums\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Samuel Štancl", + "email": "samuel@archte.ch" + } + ], + "description": "Helpers for making PHP enums more lovable.", + "support": { + "issues": "https://github.com/archtechx/enums/issues", + "source": "https://github.com/archtechx/enums/tree/v1.1.2" + }, + "time": "2025-06-06T23:15:09+00:00" + }, { "name": "brianium/paratest", "version": "v7.20.0", @@ -8154,6 +8207,145 @@ ], "time": "2026-03-29T15:46:14+00:00" }, + { + "name": "codezero/browser-locale", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/codezero-be/browser-locale.git", + "reference": "dd6c50e5557b06b57960df6db7f52e85ef683e5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/codezero-be/browser-locale/zipball/dd6c50e5557b06b57960df6db7f52e85ef683e5e", + "reference": "dd6c50e5557b06b57960df6db7f52e85ef683e5e", + "shasum": "" + }, + "require": { + "php": "^7.0|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "phpunit/phpunit": "^6.0|^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "CodeZero\\BrowserLocale\\Laravel\\BrowserLocaleServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "CodeZero\\BrowserLocale\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ivan Vermeyen", + "email": "ivan@codezero.be" + } + ], + "description": "Get the most preferred locales from your visitor's browser.", + "keywords": [ + "browser", + "country", + "detect", + "language", + "locale", + "php", + "website" + ], + "support": { + "issues": "https://github.com/codezero-be/browser-locale/issues", + "source": "https://github.com/codezero-be/browser-locale/tree/3.4.0" + }, + "funding": [ + { + "url": "https://paypal.me/ivanvermeyen", + "type": "custom" + }, + { + "url": "https://ko-fi.com/ivanvermeyen", + "type": "ko_fi" + } + ], + "time": "2024-03-02T12:51:17+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "c0efbeb2f3fd7e23c7f180a59765c0b48a31fbc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/c0efbeb2f3fd7e23c7f180a59765c0b48a31fbc4", + "reference": "c0efbeb2f3fd7e23c7f180a59765c0b48a31fbc4", + "shasum": "" + }, + "require": { + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6 || ^7 || ^8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.8.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2026-09-25T15:24:19+00:00" + }, { "name": "composer/pcre", "version": "3.4.0", @@ -8230,6 +8422,83 @@ ], "time": "2026-06-07T11:47:49+00:00" }, + { + "name": "composer/semver", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "f7a296f4c4cf8cb8bb83e35d6951a406bb11afa5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/f7a296f4c4cf8cb8bb83e35d6951a406bb11afa5", + "reference": "f7a296f4c4cf8cb8bb83e35d6951a406bb11afa5", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Version comparison library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.5.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2026-09-24T14:38:51+00:00" + }, { "name": "composer/xdebug-handler", "version": "3.0.5", @@ -8296,6 +8565,233 @@ ], "time": "2024-05-06T16:37:16+00:00" }, + { + "name": "dragon-code/contracts", + "version": "2.25.0", + "source": { + "type": "git", + "url": "https://github.com/TheDragonCode/contracts.git", + "reference": "13d1254801026be5ba33cf1309a414953869175f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TheDragonCode/contracts/zipball/13d1254801026be5ba33cf1309a414953869175f", + "reference": "13d1254801026be5ba33cf1309a414953869175f", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-message": "^1.0.1 || ^2.0", + "symfony/http-kernel": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/polyfill-php80": "^1.23" + }, + "conflict": { + "andrey-helldar/contracts": "*" + }, + "require-dev": { + "illuminate/database": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "phpdocumentor/reflection-docblock": "^5.0 || ^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "DragonCode\\Contracts\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro" + } + ], + "description": "A set of contracts for any project", + "keywords": [ + "contracts", + "interfaces" + ], + "support": { + "source": "https://github.com/TheDragonCode/contracts" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-03-17T21:50:20+00:00" + }, + { + "name": "dragon-code/pretty-array", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/TheDragonCode/pretty-array.git", + "reference": "b94034d92172a5d14a578822d68b2a8f8b5388e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TheDragonCode/pretty-array/zipball/b94034d92172a5d14a578822d68b2a8f8b5388e0", + "reference": "b94034d92172a5d14a578822d68b2a8f8b5388e0", + "shasum": "" + }, + "require": { + "dragon-code/contracts": "^2.20", + "dragon-code/support": "^6.11.2", + "ext-dom": "*", + "ext-mbstring": "*", + "php": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^10.0 || ^11.0 || ^12.0" + }, + "suggest": { + "symfony/thanks": "Give thanks (in the form of a GitHub) to your fellow PHP package maintainers" + }, + "type": "library", + "autoload": { + "psr-4": { + "DragonCode\\PrettyArray\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro" + } + ], + "description": "Simple conversion of an array to a pretty view", + "keywords": [ + "andrey helldar", + "array", + "dragon", + "dragon code", + "pretty", + "pretty array" + ], + "support": { + "issues": "https://github.com/TheDragonCode/pretty-array/issues", + "source": "https://github.com/TheDragonCode/pretty-array" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2025-02-24T15:35:24+00:00" + }, + { + "name": "dragon-code/support", + "version": "6.17.1", + "source": { + "type": "git", + "url": "https://github.com/TheDragonCode/support.git", + "reference": "82a465953267989883d64b921e9725600a5073b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TheDragonCode/support/zipball/82a465953267989883d64b921e9725600a5073b5", + "reference": "82a465953267989883d64b921e9725600a5073b5", + "shasum": "" + }, + "require": { + "dragon-code/contracts": "^2.22.0", + "ext-bcmath": "*", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.1", + "psr/http-message": "^1.0.1 || ^2.0", + "voku/portable-ascii": "^1.4.8 || ^2.0.1" + }, + "conflict": { + "andrey-helldar/support": "*" + }, + "require-dev": { + "illuminate/contracts": "^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0", + "phpunit/phpunit": "^9.6 || ^11.0 || ^12.0", + "symfony/var-dumper": "^6.0 || ^7.0" + }, + "suggest": { + "dragon-code/laravel-support": "Various helper files for the Laravel and Lumen frameworks", + "symfony/thanks": "Give thanks (in the form of a GitHub) to your fellow PHP package maintainers" + }, + "type": "library", + "extra": { + "dragon-code": { + "docs-generator": { + "preview": { + "brand": "php", + "vendor": "The Dragon Code" + } + } + } + }, + "autoload": { + "psr-4": { + "DragonCode\\Support\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro" + } + ], + "description": "Support package is a collection of helpers and tools for any project.", + "keywords": [ + "dragon", + "dragon-code", + "framework", + "helper", + "helpers", + "laravel", + "php", + "support", + "symfony", + "yii", + "yii2" + ], + "support": { + "issues": "https://github.com/TheDragonCode/support/issues", + "source": "https://github.com/TheDragonCode/support" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-04-03T15:06:29+00:00" + }, { "name": "fakerphp/faker", "version": "v1.24.1", @@ -8737,6 +9233,1368 @@ ], "time": "2026-09-19T10:46:58+00:00" }, + { + "name": "laravel-lang/actions", + "version": "1.13.2", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/actions.git", + "reference": "5e9bbd3b8af3fed4b733e424c56810b0d3e81377" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/actions/zipball/5e9bbd3b8af3fed4b733e424c56810b0d3e81377", + "reference": "5e9bbd3b8af3fed4b733e424c56810b0d3e81377", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^14.0 || ^15.0 || ^16.0", + "php": "^8.1" + }, + "require-dev": { + "laravel-lang/status-generator": "^2.3.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "symfony/var-dumper": "^6.3 || ^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Actions\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Actions\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro" + }, + { + "name": "Laravel Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "Translation of buttons and other action elements", + "keywords": [ + "actions", + "buttons", + "lang", + "languages", + "laravel", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/actions/issues", + "source": "https://github.com/Laravel-Lang/actions/tree/1.13.2" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-05-30T12:11:11+00:00" + }, + { + "name": "laravel-lang/attributes", + "version": "2.16.4", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/attributes.git", + "reference": "4a0de72632bb1862532977fd76ed6be9c1cc65af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/attributes/zipball/4a0de72632bb1862532977fd76ed6be9c1cc65af", + "reference": "4a0de72632bb1862532977fd76ed6be9c1cc65af", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^14.0 || ^15.0 || ^16.0", + "php": "^8.1" + }, + "require-dev": { + "laravel-lang/status-generator": "^1.19 || ^2.0", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "symfony/var-dumper": "^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Attributes\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Attributes\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "Translation of form element names", + "keywords": [ + "attributes", + "fields", + "form", + "lang", + "languages", + "laravel", + "messages", + "translations", + "validation" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/attributes/issues", + "source": "https://github.com/Laravel-Lang/attributes/tree/2.16.4" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-05-30T12:11:05+00:00" + }, + { + "name": "laravel-lang/common", + "version": "6.8.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/common.git", + "reference": "f1e3383f94c4f157f0f41324792382bf2dd84d52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/common/zipball/f1e3383f94c4f157f0f41324792382bf2dd84d52", + "reference": "f1e3383f94c4f157f0f41324792382bf2dd84d52", + "shasum": "" + }, + "require": { + "laravel-lang/actions": "^1.9", + "laravel-lang/attributes": "^2.12", + "laravel-lang/http-statuses": "^3.8", + "laravel-lang/json-fallback": "^2.2", + "laravel-lang/lang": "^13.12 || ^14.0 || ^15.5.1", + "laravel-lang/locales": "^2.10", + "laravel-lang/models": "^1.3", + "laravel-lang/moonshine": "^1.2", + "laravel-lang/publisher": "^16.5", + "laravel-lang/routes": "^1.7", + "laravel-lang/starter-kits": "^1.0", + "php": "^8.1" + }, + "require-dev": { + "dragon-code/support": "^6.13", + "orchestra/testbench": "^8.17 || ^9.1.2 || ^10.0 || ^11.0", + "phpunit/phpunit": "^10.5.20 || ^11.0 || ^12.0", + "symfony/var-dumper": "^6.4 || ^7.1.1 || ^8.0" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + }, + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro" + } + ], + "description": "Easily connect the necessary language packs to the application", + "keywords": [ + "Laravel-lang", + "actions", + "attribute", + "attributes", + "breeze", + "buttons", + "cashier", + "fortify", + "framework", + "http", + "http-status", + "http-status-code", + "i18n", + "jetstream", + "lang", + "language", + "languages", + "laravel", + "locale", + "locales", + "localization", + "localizations", + "moonshine", + "nova", + "publisher", + "spark", + "translation", + "translations", + "ui" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/common/issues", + "source": "https://github.com/Laravel-Lang/common" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-03-17T23:15:14+00:00" + }, + { + "name": "laravel-lang/config", + "version": "1.17.1", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/config.git", + "reference": "7970b89045998b73aab4ffd99a04c6f7f5b3f4f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/config/zipball/7970b89045998b73aab4ffd99a04c6f7f5b3f4f8", + "reference": "7970b89045998b73aab4ffd99a04c6f7f5b3f4f8", + "shasum": "" + }, + "require": { + "archtechx/enums": "^1.0", + "illuminate/config": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/locale-list": "^1.6", + "php": "^8.1" + }, + "require-dev": { + "orchestra/testbench": "^8.23 || ^9.1 || ^10.0 || ^11.0", + "pestphp/pest": "^2.34 || ^3.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Config\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Config\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "The Laravel-Lang config package", + "keywords": [ + "Laravel-lang", + "Settings", + "config", + "lang", + "languages", + "laravel", + "locale", + "locales", + "localization", + "localizations", + "translation", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/config/issues", + "source": "https://github.com/Laravel-Lang/config/tree/1.17.1" + }, + "time": "2026-09-20T09:41:38+00:00" + }, + { + "name": "laravel-lang/http-statuses", + "version": "3.13.2", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/http-statuses.git", + "reference": "2d33c7b4f5b268224c559c27c1220764b5391ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/http-statuses/zipball/2d33c7b4f5b268224c559c27c1220764b5391ae2", + "reference": "2d33c7b4f5b268224c559c27c1220764b5391ae2", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^14.1 || ^15.0 || ^16.0", + "php": "^8.1" + }, + "require-dev": { + "laravel-lang/status-generator": "^1.19 || ^2.0", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "symfony/var-dumper": "^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\HttpStatuses\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\HttpStatuses\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "Translation of HTTP statuses", + "keywords": [ + "http", + "lang", + "languages", + "laravel", + "messages", + "status", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/http-statuses/issues", + "source": "https://github.com/Laravel-Lang/http-statuses/tree/3.13.2" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-05-30T12:11:06+00:00" + }, + { + "name": "laravel-lang/json-fallback", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/json-fallback.git", + "reference": "1b02c798da837c63e2d01f45b45bb49367d5b6a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/json-fallback/zipball/1b02c798da837c63e2d01f45b45bb49367d5b6a1", + "reference": "1b02c798da837c63e2d01f45b45bb49367d5b6a1", + "shasum": "" + }, + "require": { + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/translation": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "php": "^8.1" + }, + "require-dev": { + "orchestra/testbench": "^8.0 || ^9.0 || ^10.0 || ^11.0", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "LaravelLang\\JsonFallback\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://github.com/andrey-helldar" + }, + { + "name": "Felipe Dsdev", + "homepage": "https://github.com/felipe-dsdev" + } + ], + "description": "Adds support for fallback JSON string translation", + "support": { + "issues": "https://github.com/Laravel-Lang/json-fallback/issues", + "source": "https://github.com/Laravel-Lang/json-fallback/tree/2.3.0" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-03-17T21:18:08+00:00" + }, + { + "name": "laravel-lang/lang", + "version": "15.37.2", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/lang.git", + "reference": "40c4749c96709e1d2da12860d4f6c249b2d52e43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/40c4749c96709e1d2da12860d4f6c249b2d52e43", + "reference": "40c4749c96709e1d2da12860d4f6c249b2d52e43", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^16.0", + "php": "^8.2" + }, + "conflict": { + "laravel/framework": "<11.0.7" + }, + "require-dev": { + "dragon-code/codestyler": "^6.0", + "laravel-lang/status-generator": "^2.11", + "phpunit/phpunit": "^11.0 || ^12.0", + "symfony/var-dumper": "^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Lang\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Lang\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "List of 126 languages for Laravel Framework, Laravel Jetstream, Laravel Fortify, Laravel Breeze, Laravel Cashier, Laravel Nova, Laravel Spark and Laravel UI", + "keywords": [ + "lang", + "languages", + "laravel", + "lpm" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/lang/issues", + "source": "https://github.com/Laravel-Lang/lang" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-09-30T11:01:30+00:00" + }, + { + "name": "laravel-lang/locale-list", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/locale-list.git", + "reference": "48e61c7f0a957420d4aaf5d35653889c25c4e2d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/locale-list/zipball/48e61c7f0a957420d4aaf5d35653889c25c4e2d4", + "reference": "48e61c7f0a957420d4aaf5d35653889c25c4e2d4", + "shasum": "" + }, + "require": { + "archtechx/enums": "^0.3.2 || ^1.0", + "php": "^8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "LaravelLang\\LocaleList\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "List of localizations available in Laravel Lang projects", + "keywords": [ + "Laravel-lang", + "lang", + "languages", + "laravel", + "locale", + "locales", + "localization", + "translation", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/locale-list/issues", + "source": "https://github.com/Laravel-Lang/locale-list" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-01-20T08:17:15+00:00" + }, + { + "name": "laravel-lang/locales", + "version": "2.11.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/locales.git", + "reference": "761aa3cfbc5bbe29eb958c9839e7dd3806193bac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/locales/zipball/761aa3cfbc5bbe29eb958c9839e7dd3806193bac", + "reference": "761aa3cfbc5bbe29eb958c9839e7dd3806193bac", + "shasum": "" + }, + "require": { + "archtechx/enums": "^0.3.2 || ^1.0", + "dragon-code/support": "^6.11.3", + "ext-json": "*", + "illuminate/collections": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/config": "^1.12", + "laravel-lang/locale-list": "^1.5", + "laravel-lang/native-country-names": "^1.5", + "laravel-lang/native-currency-names": "^1.6", + "laravel-lang/native-locale-names": "^2.5", + "php": "^8.1" + }, + "require-dev": { + "orchestra/testbench": "^8.0 || ^9.0 || ^10.0 || ^11.0", + "pestphp/pest": "^2.24.1 || ^3.0 || ^4.0", + "symfony/var-dumper": "^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Locales\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Locales\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "Basic functionality for working with localizations", + "keywords": [ + "laravel", + "locale", + "locales", + "localization", + "translation", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/locales/issues", + "source": "https://github.com/Laravel-Lang/locales" + }, + "time": "2026-03-17T22:40:41+00:00" + }, + { + "name": "laravel-lang/models", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/models.git", + "reference": "a9aa0205ca019f4459ace26b0432b3b6d1886f66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/models/zipball/a9aa0205ca019f4459ace26b0432b3b6d1886f66", + "reference": "a9aa0205ca019f4459ace26b0432b3b6d1886f66", + "shasum": "" + }, + "require": { + "composer/class-map-generator": "^1.3", + "dragon-code/support": "^6.13", + "illuminate/database": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/config": "^1.12", + "laravel-lang/locales": "^2.10.0", + "laravel/prompts": ">=0.1", + "php": "^8.1" + }, + "require-dev": { + "laravel/pint": "^1.20", + "orchestra/testbench": "^8.0 || ^9.0 || ^10.0 || ^11.0", + "pestphp/pest": "^2.34 || ^3.0 || ^4.0", + "pestphp/pest-plugin-laravel": "^2.4 || ^3.0 || ^4.0", + "symfony/var-dumper": "^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Models\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Models\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro", + "role": "Maintainer" + }, + { + "name": "Andrey Sokolov", + "email": "walfireru@gmail.com", + "homepage": "https://github.com/Quiss", + "role": "Usability consultant" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "Easy and fast way to localize models", + "keywords": [ + "database", + "l18n", + "languages", + "laravel", + "locales", + "localization", + "models", + "translate", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/models/issues", + "source": "https://github.com/Laravel-Lang/models" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-03-17T23:00:20+00:00" + }, + { + "name": "laravel-lang/moonshine", + "version": "1.9.2", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/moonshine.git", + "reference": "3927bcf658d392c9f2eb03c773f52ff650b37f63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/moonshine/zipball/3927bcf658d392c9f2eb03c773f52ff650b37f63", + "reference": "3927bcf658d392c9f2eb03c773f52ff650b37f63", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^15.0 || ^16.4.1", + "php": "^8.1" + }, + "require-dev": { + "dragon-code/support": "^6.15.2", + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/lang": "^14.8 || ^15.14", + "laravel-lang/status-generator": "^2.12", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "symfony/var-dumper": "^6.3 || ^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\MoonShine\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\MoonShine\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "Translation for MoonShine admin panel", + "keywords": [ + "lang", + "languages", + "laravel", + "moonshine", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/moonshine/issues", + "source": "https://github.com/Laravel-Lang/moonshine/tree/1.9.2" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-05-30T12:11:41+00:00" + }, + { + "name": "laravel-lang/native-country-names", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/native-country-names.git", + "reference": "1d293138e34eb9e914bc4568cdebac2cb0a2eb0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/native-country-names/zipball/1d293138e34eb9e914bc4568cdebac2cb0a2eb0e", + "reference": "1d293138e34eb9e914bc4568cdebac2cb0a2eb0e", + "shasum": "" + }, + "require": { + "dragon-code/support": "^6.11", + "ext-json": "*", + "illuminate/collections": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/locale-list": "^1.5", + "pestphp/pest": "^2.0 || ^3.0 || ^4.0", + "punic/punic": "^3.8", + "symfony/console": "^6.0 || ^7.0 || ^8.0", + "symfony/process": "^6.0 || ^7.0 || ^8.0", + "symfony/var-dumper": "^6.0 || ^7.0 || ^8.0", + "vlucas/phpdotenv": "^5.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "LaravelLang\\NativeCountryNames\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "The project contains native translations of country names", + "keywords": [ + "Laravel-lang", + "countries", + "country", + "lang", + "languages", + "laravel", + "locale", + "locales", + "localization", + "territories", + "territory", + "translation", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/native-country-names/issues", + "source": "https://github.com/Laravel-Lang/native-country-names" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-03-17T22:01:15+00:00" + }, + { + "name": "laravel-lang/native-currency-names", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/native-currency-names.git", + "reference": "7ebe95a9942bebf6afd61986fa18c82dade3d583" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/native-currency-names/zipball/7ebe95a9942bebf6afd61986fa18c82dade3d583", + "reference": "7ebe95a9942bebf6afd61986fa18c82dade3d583", + "shasum": "" + }, + "require": { + "dragon-code/support": "^6.11", + "ext-json": "*", + "illuminate/collections": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/locale-list": "^1.2", + "pestphp/pest": "^2.0 || ^3.0 || ^4.0", + "punic/punic": "^3.8", + "symfony/console": "^6.0 || ^7.0", + "symfony/process": "^6.0 || ^7.0", + "symfony/var-dumper": "^6.0 || ^7.0", + "vlucas/phpdotenv": "^5.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "LaravelLang\\NativeCurrencyNames\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "The project contains native translations of currency names", + "keywords": [ + "Laravel-lang", + "currency", + "lang", + "languages", + "laravel", + "locale", + "locales", + "localization", + "translation", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/native-currency-names/issues", + "source": "https://github.com/Laravel-Lang/native-currency-names" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-06-20T16:50:37+00:00" + }, + { + "name": "laravel-lang/native-locale-names", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/native-locale-names.git", + "reference": "e5925182bad34654203a5d93544357ca00a6d9c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/native-locale-names/zipball/e5925182bad34654203a5d93544357ca00a6d9c0", + "reference": "e5925182bad34654203a5d93544357ca00a6d9c0", + "shasum": "" + }, + "require": { + "dragon-code/support": "^6.11", + "ext-json": "*", + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/locale-list": "^1.2", + "pestphp/pest": "^2.24.3 || ^3.0 || ^4.0", + "punic/punic": "^3.8", + "symfony/console": "^6.0 || ^7.0", + "symfony/process": "^6.0 || ^7.0", + "symfony/var-dumper": "^6.0 || ^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "LaravelLang\\NativeLocaleNames\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "The project contains native translations of locale names", + "keywords": [ + "Laravel-lang", + "lang", + "languages", + "laravel", + "locale", + "locales", + "localization", + "translation", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/native-locale-names/issues", + "source": "https://github.com/Laravel-Lang/native-locale-names" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-06-20T16:52:11+00:00" + }, + { + "name": "laravel-lang/publisher", + "version": "16.8.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/publisher.git", + "reference": "e5d3383f5385c2102f8a0d3dbe488ed86cd0250f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/publisher/zipball/e5d3383f5385c2102f8a0d3dbe488ed86cd0250f", + "reference": "e5d3383f5385c2102f8a0d3dbe488ed86cd0250f", + "shasum": "" + }, + "require": { + "composer/semver": "^3.4", + "dragon-code/pretty-array": "^4.1", + "dragon-code/support": "^6.11.3", + "ext-json": "*", + "illuminate/collections": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/console": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/config": "^1.12", + "laravel-lang/locales": "^2.10", + "league/commonmark": "^2.4.1", + "league/config": "^1.2", + "php": "^8.1" + }, + "conflict": { + "laravel-lang/attributes": "<2.0", + "laravel-lang/http-statuses": "<3.0", + "laravel-lang/lang": "<11.0" + }, + "require-dev": { + "laravel-lang/json-fallback": "^2.2", + "orchestra/testbench": "^8.14 || ^9.0 || ^10.0 || ^11.0", + "phpunit/phpunit": "^10.4.2 || ^11.0 || ^12.0", + "symfony/var-dumper": "^6.3.6 || ^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Publisher\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Publisher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "Localization publisher for your Laravel application", + "keywords": [ + "Laravel-lang", + "breeze", + "cashier", + "fortify", + "framework", + "i18n", + "jetstream", + "lang", + "languages", + "laravel", + "locale", + "locales", + "localization", + "localizations", + "lpm", + "lumen", + "nova", + "publisher", + "spark", + "trans", + "translation", + "translations", + "validations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/publisher/issues", + "source": "https://github.com/Laravel-Lang/publisher" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-03-17T22:56:20+00:00" + }, + { + "name": "laravel-lang/routes", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/routes.git", + "reference": "29d7f83f2679f51187aa4e761d73bb4e0d133bf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/routes/zipball/29d7f83f2679f51187aa4e761d73bb4e0d133bf4", + "reference": "29d7f83f2679f51187aa4e761d73bb4e0d133bf4", + "shasum": "" + }, + "require": { + "codezero/browser-locale": "^3.4", + "dragon-code/support": "^6.13", + "illuminate/config": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/http": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/routing": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "laravel-lang/config": "^1.13", + "laravel-lang/locales": "^2.10", + "php": "^8.1" + }, + "require-dev": { + "orchestra/testbench": "^8.23 || ^9.1 || ^10.0 || ^11.0", + "pestphp/pest": "^2.34 || ^3.0 || ^4.0", + "pestphp/pest-plugin-laravel": "^2.4 || ^3.0 || ^4.0", + "symfony/var-dumper": "^6.0 || ^7.0 || ^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Routes\\ServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "helpers/functions.php" + ], + "psr-4": { + "LaravelLang\\Routes\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "Easy and fast way to localize routes", + "keywords": [ + "l18n", + "languages", + "laravel", + "localization", + "routes", + "translate", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/routes/issues", + "source": "https://github.com/Laravel-Lang/routes" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-03-17T23:06:16+00:00" + }, + { + "name": "laravel-lang/starter-kits", + "version": "1.16.1", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/starter-kits.git", + "reference": "87c00c77bbb73385d0526b35dbe07ea2bbfc32db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/starter-kits/zipball/87c00c77bbb73385d0526b35dbe07ea2bbfc32db", + "reference": "87c00c77bbb73385d0526b35dbe07ea2bbfc32db", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^16.6", + "php": "^8.1" + }, + "require-dev": { + "laravel-lang/status-generator": "^2.13", + "phpunit/phpunit": "^11.0 || ^12.0", + "symfony/var-dumper": "^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\StarterKits\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\StarterKits\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel Lang Team", + "homepage": "https://laravel-lang.com" + } + ], + "description": "Translations for Laravel Starter Kits", + "keywords": [ + "i18n", + "lang", + "languages", + "laravel", + "starter-kit", + "starter-kits", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/starter-kits/issues", + "source": "https://github.com/Laravel-Lang/starter-kits/tree/1.16.1" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2026-07-16T18:13:26+00:00" + }, { "name": "laravel/agent-detector", "version": "v2.0.2", diff --git a/config/app.php b/config/app.php index 1f8dd27..ca2e1b7 100644 --- a/config/app.php +++ b/config/app.php @@ -78,10 +78,21 @@ return [ | */ - 'locale' => env('APP_LOCALE', 'en'), + 'locale' => env('APP_LOCALE', 'de'), 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + /* + | Sprachen, die Benutzer in den Einstellungen wählen können (Code => Bezeichnung + | in der jeweiligen Sprache). Für jede Sprache außer "en" muss lang/{code}.json + | alle Texte enthalten, siehe TranslationCoverageTest. + */ + + 'available_locales' => [ + 'de' => 'Deutsch', + 'en' => 'English', + ], + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), /* diff --git a/database/migrations/2026_10_01_074749_add_locale_to_users_table.php b/database/migrations/2026_10_01_074749_add_locale_to_users_table.php new file mode 100644 index 0000000..f132c35 --- /dev/null +++ b/database/migrations/2026_10_01_074749_add_locale_to_users_table.php @@ -0,0 +1,29 @@ +string('locale', 10)->nullable()->after('email'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('locale'); + }); + } +}; diff --git a/docs/tech-stack.md b/docs/tech-stack.md index 29898de..2a280e2 100644 --- a/docs/tech-stack.md +++ b/docs/tech-stack.md @@ -15,6 +15,7 @@ und benennt, wo KI-BauIN bewusst abweicht und warum. | Tailwind CSS 4 | Gestaltung | gleich | | TallStackUI 4 (Präfix `ts-`) | UI-Komponenten | Version abweichend (Referenz: 3) | | Laravel Fortify | Login, Passwort-Reset, E-Mail-Bestätigung, 2FA, Passkeys | ergänzt | +| Mehrsprachigkeit | Deutsch (Standard) und Englisch, Sprache je Benutzer; Übersetzungen über Laravel Lang (nur Entwicklung), eigene Texte in `lang/project/` | ergänzt | | Vite 8, Node.js/npm | Asset-Build | gleich | | Composer | Abhängigkeiten mit Lockfile | gleich | | PhpSpreadsheet | Excel-Export der Bewertungsmatrix | geplant, sobald die Vorlage vorliegt | @@ -102,7 +103,7 @@ Ergänzt bzw. anders als in der Referenz: - Ist 2FA für alle Benutzer Pflicht? - Verlangt die IT des Kunden eine Festplattenverschlüsselung auf der VM? - Speicherort und Aufbewahrungsdauer der Backups. -- Deutsche Übersetzung der Oberflächentexte (das Starter-Kit liefert englische Texte). +- Weitere Sprachen über Deutsch und Englisch hinaus (technisch vorbereitet, fachlich nicht angefragt). ## Architekturübersicht diff --git a/lang/de.json b/lang/de.json new file mode 100644 index 0000000..73f6a1e --- /dev/null +++ b/lang/de.json @@ -0,0 +1,426 @@ +{ + "(and :count more error)": "(und :count weiterer Fehler)", + "(and :count more errors)": "(und :count weiterer Fehler)|(und :count weitere Fehler)|(und :count weitere Fehler)", + "2FA recovery codes": "2FA-Wiederherstellungscodes", + ":action to join the \":team\" team.": ":action to join the \":team\" team.", + ":inviter invited you to join this team.": ":inviter invited you to join this team.", + ":inviterName has invited you to join the :teamName team.": ":inviterName hat Sie eingeladen, dem Team :teamName beizutreten.", + "A decryption key is required.": "Ein Entschlüsselungsschlüssel ist nötig.", + "A new verification link has been sent to the email address you provided during registration.": "Ein neuer Bestätigungslink wurde an die E-Mail-Adresse gesendet, die Sie bei der Registrierung angegeben haben.", + "A new verification link has been sent to your email address.": "Ein neuer Bestätigungslink wurde an Ihre E-Mail-Adresse versendet.", + "A Timeout Occurred": "Eine Zeitüberschreitung ist aufgetreten", + "Accept": "Akzeptieren", + "Accept or decline the teams you have been invited to join.": "Nehmen Sie die Einladungen zu den Teams, zu denen Sie eingeladen wurden, an oder lehnen Sie sie ab.", + "Accepted": "Akzeptiert", + "Action": "Aktion", + "Actions": "Aktionen", + "Add": "Hinzufügen", + "Add :name": ":name hinzufügen", + "Add a passkey to sign in without a password": "Fügen Sie einen Passkey hinzu, um sich ohne Passwort anzumelden.", + "Add passkey": "Passkey hinzufügen", + "Added :time": "Hinzugefügt :time", + "Admin": "Administrator", + "Agree": "Zustimmen", + "All rights reserved.": "Alle Rechte vorbehalten.", + "Already have an account?": "Haben Sie bereits ein Konto?", + "Already Reported": "Bereits gemeldet", + "An invitation has already been sent to this email address.": "An diese E-Mail-Adresse wurde bereits eine Einladung gesendet.", + "Appearance": "Darstellung", + "Appearance settings": "Darstellungseinstellungen", + "Archive": "Archiv", + "Are you sure you want to cancel the invitation for :email?": "Möchten Sie die Einladung für :email wirklich abbrechen?", + "Are you sure you want to delete your account?": "Möchten Sie Ihr Konto wirklich löschen?", + "Are you sure you want to leave :name?": "Sind Sie sicher, dass Sie :name verlassen möchten?", + "Are you sure you want to remove :name from this team?": "Möchten Sie :name wirklich aus diesem Team entfernen möchten?", + "Are you sure you want to remove the passkey \":name\"? You will no longer be able to use it to sign in.": "Sind Sie sicher, dass Sie den Passkey \":name\" entfernen möchten? Sie werden ihn nicht mehr für die Anmeldung verwenden können.", + "Are you sure?": "Sind Sie sicher?", + "Assign": "Zuordnen", + "Associate": "Assoziieren", + "Attach": "Anhängen", + "Authenticating...": "Anmeldung läuft …", + "Authentication code": "Authentifizierungscode", + "Back": "Zurück", + "Bad Gateway": "Fehlerhaftes Gateway", + "Bad Request": "Ungültige Anfrage", + "Bandwidth Limit Exceeded": "Bandbreitenlimit überschritten", + "Browse": "Durchsuchen", + "Cancel": "Abbrechen", + "Cancel invitation": "Einladung abbrechen", + "Choose": "Wählen Sie", + "Choose :name": "Wählen Sie :name", + "Choose File": "Datei wählen", + "Choose Image": "Bild wählen", + "Click here to re-send the verification email.": "Klicken Sie hier, um die Bestätigungs-E-Mail erneut zu senden.", + "Click to copy": "Klicken Sie zum Kopieren", + "Client Closed Request": "Client hat die Anfrage geschlossen", + "Close": "Schließen", + "Collapse": "Zusammenklappen", + "Collapse All": "Alle zusammenklappen", + "Comment": "Kommentar", + "Confirm": "Bestätigen", + "Confirm password": "Passwort bestätigen", + "Confirm with passkey": "Mit Passkey bestätigen", + "Confirming...": "Bestätigung läuft …", + "Conflict": "Konflikt", + "Connect": "Verbinden", + "Connection Closed Without Response": "Verbindung ohne Antwort getrennt", + "Connection Timed Out": "Verbindungszeit überschritten", + "Continue": "Weiter", + "Create": "Erstellen", + "Create :name": ":name erstellen", + "Create a new team": "Neues Team erstellen", + "Create account": "Konto erstellen", + "Create an account": "Ein Konto erstellen", + "Create team": "Team erstellen", + "Created": "Erstellt", + "Current password": "Aktuelles Passwort", + "Dark": "Dunkel", + "Dashboard": "Dashboard", + "Decline": "Ablehnen", + "Delete": "Löschen", + "Delete :name": ":name löschen", + "Delete account": "Konto löschen", + "Delete team": "Team löschen", + "Delete your account and all of its resources": "Löschen Sie Ihr Konto und alle zugehörigen Ressourcen", + "Detach": "Trennen", + "Details": "Details", + "Disable": "Deaktivieren", + "Disable 2FA": "2FA deaktivieren", + "Discard": "Verwerfen", + "Documentation": "Dokumentation", + "Don't have an account?": "Sie haben noch kein Konto?", + "Done": "Erledigt", + "Down": "Runter", + "Duplicate": "Duplizieren", + "Duplicate :name": ":name duplizieren", + "e.g., MacBook Pro, iPhone": "z. B. MacBook Pro, iPhone", + "Each recovery code can be used once to access your account and will be removed after use. If you need more, click Regenerate codes above.": "Jeder Wiederherstellungscode kann einmal für den Zugriff auf Ihr Konto verwendet werden und wird nach der Verwendung gelöscht. Wenn Sie weitere Codes benötigen, klicken Sie oben auf „Codes neu generieren“.", + "Edit": "Bearbeiten", + "Edit :name": ":name bearbeiten", + "Edit team": "Team bearbeiten", + "Email": "E-Mail", + "Email address": "E-Mail-Adresse", + "Email password reset link": "Link zum Zurücksetzen senden", + "Email verification": "E-Mail-Bestätigung", + "Enable": "Aktivieren", + "Enable 2FA": "2FA aktivieren", + "Enable two-factor authentication": "Zwei-Faktor-Authentifizierung aktivieren", + "Encrypted environment file already exists.": "Verschlüsselte Umgebungsdatei ist bereits vorhanden.", + "Encrypted environment file not found.": "Verschlüsselte Umgebungsdatei nicht gefunden.", + "Ensure your account is using a long, random password to stay secure": "Stellen Sie sicher, dass Ihr Konto ein langes, zufälliges Passwort verwendet, um sicher zu bleiben", + "Enter the 6-digit code from your authenticator app.": "Geben Sie den 6-stelligen Code aus Ihrer Authentifizierungs-App ein.", + "Enter the authentication code provided by your authenticator application.": "Geben Sie den von Ihrer Authentifizierungsanwendung bereitgestellten Authentifizierungscode ein.", + "Enter your details below to create your account": "Geben Sie unten Ihre Daten ein, um Ihr Konto zu erstellen", + "Enter your email and password below to log in": "Geben Sie unten Ihre E-Mail-Adresse und Ihr Passwort ein, um sich anzumelden", + "Enter your email to receive a password reset link": "Geben Sie Ihre E-Mail-Adresse ein, um einen Link zum Zurücksetzen des Passworts zu erhalten", + "Environment file already exists.": "Umgebungsdatei ist bereits vorhanden.", + "Environment file not found.": "Umgebungsdatei nicht gefunden.", + "errors": "Fehler", + "Expand": "Erweitern", + "Expand All": "Alle erweitern", + "Expectation Failed": "Erwartung gescheitert", + "Explanation": "Erläuterung", + "Export": "Exportieren", + "Export :name": ":name exportieren", + "Failed Dependency": "Fehlgeschlagene Abhängigkeit", + "File": "Datei", + "Files": "Dateien", + "Forbidden": "Verboten", + "Forgot password": "Passwort vergessen", + "Forgot your password?": "Passwort vergessen?", + "Found": "Gefunden", + "Full name": "Vollständiger Name", + "Gateway Timeout": "Gateway-Zeitüberschreitung", + "Give this passkey a name to help you identify it later.": "Geben Sie diesem Passkey einen Namen, damit Sie ihn später leichter identifizieren können.", + "Give your team a name to get started.": "Geben Sie Ihrem Team einen Namen, um loszulegen.", + "Go Home": "Zur Startseite", + "Go to page :page": "Gehe zur Seite :page", + "Gone": "Nicht mehr verfügbar", + "Hello!": "Hallo!", + "Hide": "Verstecken", + "Hide :name": ":name ausblenden", + "Hide recovery codes": "Wiederherstellungscodes ausblenden", + "Home": "Startseite", + "HTTP Version Not Supported": "HTTP Version nicht unterstützt", + "I'm a teapot": "Ich bin eine Teekanne", + "If you did not create an account, no further action is required.": "Wenn Sie kein Konto erstellt haben, sind keine weiteren Handlungen nötig.", + "If you did not request a password reset, no further action is required.": "Wenn Sie kein Zurücksetzen des Passworts beantragt haben, sind keine weiteren Handlungen nötig.", + "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Sollten Sie Schwierigkeiten haben, die Schaltfläche \":actionText\" zu klicken, kopieren Sie den nachfolgenden Link\n in Ihre Adresszeile des Browsers.", + "IM Used": "IM verwendet", + "Image": "Bild", + "Impersonate": "Imitieren", + "Impersonation": "Identitätswechsel", + "Import": "Importieren", + "Import :name": "Importieren Sie :name", + "Insufficient Storage": "Nicht genügend Speicherplatz", + "Internal Server Error": "Interner Serverfehler", + "Introduction": "Einführung", + "Invalid credential format.": "Ungültiges Format der Anmeldedaten.", + "Invalid filename.": "Ungültiger Dateiname.", + "Invalid JSON was returned from the route.": "Von der Route wurde ein ungültiger JSON-Code zurückgegeben.", + "Invalid SSL Certificate": "Ungültiges SSL-Zertifikat", + "Invitation accepted.": "Einladung akzeptiert.", + "Invitation cancelled.": "Einladung abgebrochen.", + "Invitation declined.": "Einladung abgelehnt.", + "Invitation sent.": "Einladung gesendet.", + "Invitations that have not been accepted yet": "Einladungen, die noch nicht angenommen wurden", + "Invite a team member": "Ein Teammitglied einladen", + "Invite member": "Mitglied einladen", + "Keep invitation": "Einladung behalten", + "Language": "Sprache", + "Last used :time": "Zuletzt :time verwendet", + "Leave team": "Team verlassen", + "Length Required": "Längenangabe erforderlich", + "Light": "Hell", + "Like": "Wie", + "Load": "Belastung", + "Localize": "Lokalisieren", + "Location": "Standort", + "Locked": "Gesperrt", + "Log In": "Einloggen", + "Log in": "Anmelden", + "log in": "Anmeldung", + "Log in and visit your dashboard to accept or decline this invitation.": "Melden Sie sich an und besuchen Sie Ihr Dashboard, um diese Einladung anzunehmen oder abzulehnen.", + "Log in to your account": "Melden Sie sich bei Ihrem Konto an", + "Log Out": "Abmelden", + "Log out": "Abmelden", + "Login": "Anmelden", + "login using a recovery code": "sich mit einem Wiederherstellungscode anmelden", + "login using an authentication code": "sich mit einem Authentifizierungscode anmelden", + "Logout": "Abmelden", + "Loop Detected": "Endlosschleife erkannt", + "Maintenance Mode": "Wartungsmodus", + "Manage who belongs to this team": "Verwalten Sie, wer zu diesem Team gehört", + "Manage your passkeys for passwordless sign-in": "Verwalten Sie Ihre Passkeys für die passwortlose Anmeldung", + "Manage your profile and account settings": "Verwalten Sie Ihr Profil und Ihre Kontoeinstellungen", + "Manage your team settings": "Verwalten Sie Ihre Teameinstellungen", + "Manage your teams and team memberships": "Verwalten Sie Ihre Teams und Teamzugehörigkeiten", + "Manage your two-factor authentication settings": "Verwalten Sie Ihre Einstellungen für die Zwei-Faktor-Authentifizierung", + "Member removed.": "Mitglied entfernt.", + "Member role updated.": "Mitgliedsrolle aktualisiert.", + "Method Not Allowed": "Methode nicht erlaubt", + "Misdirected Request": "Fehlgeleitete Anfrage", + "Moved Permanently": "Permanent verschoben", + "Multi-Status": "Multistatus", + "Multiple Choices": "Mehrere Auswahlmöglichkeiten", + "Name": "Name", + "Network Authentication Required": "Netzwerkauthentifizierung erforderlich", + "Network Connect Timeout Error": "Zeitüberschreitungsfehler bei Netzwerkverbindung", + "Network Read Timeout Error": "Zeitüberschreitungsfehler beim Lesen des Netzwerks", + "New": "Neu", + "New :name": "Neu :name", + "New password": "Neues Passwort", + "New team": "Neues Team", + "No": "Nein", + "No Content": "Kein Inhalt", + "No passkeys yet": "Noch keine Passkeys", + "Non-Authoritative Information": "Nicht maßgebende Informationen", + "Not Acceptable": "Nicht akzeptierbar", + "Not Extended": "Nicht erweitert", + "Not Found": "Nicht gefunden", + "Not Implemented": "Nicht implementiert", + "Not Modified": "Nicht modifiziert", + "of": "von", + "OK": "OK", + "Once your account is deleted, all of its resources and data will also be permanently deleted. Please confirm you would like to permanently delete your account.": "Sobald Ihr Konto gelöscht ist, werden auch alle zugehörigen Ressourcen und Daten dauerhaft gelöscht. Bitte bestätigen Sie, dass Sie Ihr Konto dauerhaft löschen möchten.", + "Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.": "Sobald Ihr Konto gelöscht wurde, werden alle Ressourcen und Daten dauerhaft gelöscht. Bitte geben Sie Ihr Passwort zur Bestätigung ein, dass Sie Ihr Konto dauerhaft löschen möchten.", + "Open": "Offen", + "Open in a current window": "In einem aktuellen Fenster öffnen", + "Open in a new window": "In einem neuen Fenster öffnen", + "Open in a parent frame": "In einem übergeordneten Frame öffnen", + "Open in the topmost frame": "Im obersten Rahmen öffnen", + "Open on the website": "Auf der Website öffnen", + "Or confirm with password": "Oder mit Passwort bestätigen", + "Or continue with email": "Oder mit E-Mail fortfahren", + "or you can": "oder Sie können", + "or, enter the code manually": "oder geben Sie den Code manuell ein", + "Or, return to": "Oder zurück zur", + "Origin Is Unreachable": "Quelle ist nicht erreichbar", + "Page Expired": "Seite abgelaufen", + "Pagination Navigation": "Seiten-Navigation", + "Partial Content": "Teilinhalt", + "Passkey name": "Name des Passkeys", + "Passkey not recognized. It may have been removed from your account.": "Passkey wurde nicht erkannt. Er wurde möglicherweise von Ihrem Konto entfernt.", + "Passkey registration session expired. Please try again.": "Die Sitzung zur Passkey-Registrierung ist abgelaufen. Bitte versuchen Sie es erneut.", + "Passkey verification session expired. Please try again.": "Die Sitzung zur Passkey-Bestätigung ist abgelaufen. Bitte versuchen Sie es erneut.", + "Passkeys": "Passkeys", + "Passkeys are not supported in this browser.": "Passkeys werden in diesem Browser nicht unterstützt.", + "Password": "Passwort", + "Password updated.": "Passwort aktualisiert.", + "Payload Too Large": "Nutzlast zu groß", + "Payment Required": "Zahlung erforderlich", + "Pending invitations": "Ausstehende Einladungen", + "Pending team invitations": "Ausstehende Teameinladungen", + "Permanent Redirect": "Permanente Weiterleitung", + "Permanently delete your team": "Löschen Sie Ihr Team endgültig", + "Personal": "Persönlich", + "Platform": "Plattform", + "Please click the button below to verify your email address.": "Bitte klicken Sie auf die Schaltfläche, um Ihre E-Mail-Adresse zu bestätigen.", + "Please confirm access to your account by entering one of your emergency recovery codes.": "Bitte bestätigen Sie den Zugriff auf Ihr Konto, indem Sie einen Ihrer Notfall-Wiederherstellungscodes eingeben.", + "Please enter your new password below": "Bitte geben Sie nachstehend Ihr neues Passwort ein.", + "Please proceed with caution, this cannot be undone.": "Bitte gehen Sie mit Vorsicht vor, dies kann nicht rückgängig gemacht werden.", + "Please verify your email address by clicking on the link we just emailed to you.": "Bitte bestätigen Sie Ihre E-Mail-Adresse, indem Sie auf den Link klicken, den wir Ihnen gerade per E-Mail gesendet haben.", + "Precondition Failed": "Vorbedingung fehlgeschlagen", + "Precondition Required": "Voraussetzung erforderlich", + "Preview": "Vorschau", + "Price": "Preis", + "Processing": "In Bearbeitung", + "Profile": "Profil", + "Profile settings": "Profileinstellungen", + "Profile updated.": "Profil aktualisiert.", + "Proxy Authentication Required": "Proxy-Authentifizierung erforderlich", + "Railgun Error": "Railgun-Fehler", + "Range Not Satisfiable": "Bereich nicht erfüllbar", + "Record": "Aufzeichnen", + "Recovery code": "Wiederherstellungscode", + "Recovery codes": "Wiederherstellungscodes", + "Recovery codes let you regain access if you lose your 2FA device. Store them in a secure password manager.": "Mit Wiederherstellungscodes können Sie wieder Zugriff erhalten, wenn Sie Ihr 2FA-Gerät verlieren. Speichern Sie diese in einem sicheren Passwort-Manager.", + "Regards,": "Mit freundlichen Grüßen,", + "Regenerate codes": "Codes neu generieren", + "Register": "Registrieren", + "Register passkey": "Passkey registrieren", + "Registering...": "Registrierung läuft …", + "Remember me": "Angemeldet bleiben", + "Remove member": "Mitglied entfernen", + "Remove passkey": "Passkey entfernen", + "Remove team member": "Team Mitglied entfernen", + "Repository": "Repository", + "Request Header Fields Too Large": "Anfrage-Header-Felder zu groß", + "Request Timeout": "Zeitüberschreitung der Anfrage", + "Resend verification email": "Bestätigungs-E-Mail erneut senden", + "Reset Content": "Inhalt zurücksetzen", + "Reset Password": "Passwort zurücksetzen", + "Reset password": "Passwort zurücksetzen", + "Reset your password": "Setzen Sie Ihr Passwort zurück", + "Restore": "Wiederherstellen", + "Restore :name": ":name wiederherstellen", + "results": "Ergebnissen", + "Retry With": "Wiederhole mit", + "Role": "Rolle", + "Save": "Speichern", + "Save & Close": "Speichern und schließen", + "Save & Return": "Speichern und zurückgeben", + "Save :name": "Sparen Sie :name", + "Search": "Suche", + "Search :name": "Suche :name", + "Security": "Sicherheit", + "Security settings": "Sicherheitseinstellungen", + "See Other": "Siehe andere Seite", + "Select": "Wählen Sie", + "Select All": "Wählen Sie Alle", + "Select team": "Team auswählen", + "Send": "Schicken", + "Send an invitation to join this team.": "Eine Einladung für dieses Team verschicken.", + "Send invitation": "Einadlung senden", + "Server Error": "Interner Fehler", + "Service Unavailable": "Service nicht verfügbar", + "Session Has Expired": "Sitzung ist abgelaufen", + "Settings": "Einstellungen", + "Show": "Zeigen", + "Show :name": ":name anzeigen", + "Show All": "Zeige alles", + "Showing": "Zeige", + "Sign In": "Anmelden", + "Sign in with a passkey": "Mit einem Passkey anmelden", + "Sign up": "Registrieren", + "Solve": "Lösen", + "SSL Handshake Failed": "SSL Handshake fehlgeschlagen", + "Start": "Starten", + "Stop": "Stoppen", + "Submit": "Einreichen", + "Subscribe": "Abonnieren", + "Switch": "Wechseln", + "Switch To Role": "Zur Rolle wechseln", + "Switching Protocols": "Protokollwechsel", + "System": "System", + "Tag": "Stichwort", + "Tags": "Stichworte", + "Team created.": "Team erstellt.", + "Team deleted.": "Team gelöscht.", + "Team members": "Teammitglieder", + "Team name": "Teamname", + "Team updated.": "Team aktualisiert.", + "Teams": "Teams", + "Temporary Redirect": "Temporäre Weiterleitung", + "The :attribute must be at least :length characters and contain at least one number.": ":Attribute muss aus mindestens :length Zeichen bestehen und mindestens eine Zahl enthalten.", + "The :attribute must be at least :length characters and contain at least one special character and one number.": ":Attribute muss mindestens :length Zeichen lang sein und mindestens ein Sonderzeichen und eine Zahl enthalten.", + "The :attribute must be at least :length characters and contain at least one special character.": ":Attribute muss aus mindestens :length Zeichen bestehen und mindestens ein Sonderzeichen enthalten.", + "The :attribute must be at least :length characters and contain at least one uppercase character and one number.": ":Attribute muss aus mindestens :length Zeichen bestehen und mindestens einen Großbuchstaben und eine Zahl enthalten.", + "The :attribute must be at least :length characters and contain at least one uppercase character and one special character.": ":Attribute muss aus mindestens :length Zeichen bestehen und mindestens einen Großbuchstaben und ein Sonderzeichen enthalten.", + "The :attribute must be at least :length characters and contain at least one uppercase character, one number, and one special character.": ":Attribute muss aus mindestens :length Zeichen bestehen und mindestens einen Großbuchstaben, eine Zahl und ein Sonderzeichen enthalten.", + "The :attribute must be at least :length characters and contain at least one uppercase character.": ":Attribute muss aus mindestens :length Zeichen bestehen und mindestens einen Großbuchstaben enthalten.", + "The :attribute must be at least :length characters.": ":Attribute muss aus mindestens :length Zeichen bestehen.", + "The existing encrypted environment file is not in readable format. Use --force to overwrite it.": "Die vorhandene verschlüsselte Umgebungsdatei liegt nicht in einem lesbaren Format vor. Verwenden Sie --force, um sie zu überschreiben.", + "The existing encryption key is required to update the encrypted environment file.": "Der vorhandene Verschlüsselungsschlüssel wird benötigt, um die verschlüsselte Umgebungsdatei zu aktualisieren.", + "The given data was invalid.": "Die gegebenen Daten waren ungültig.", + "The provided password does not match your current password.": "Das angegebene Passwort stimmt nicht mit Ihrem aktuellen Passwort überein.", + "The provided password was incorrect.": "Das angegebene Passwort war falsch.", + "The provided two factor authentication code was invalid.": "Der angegebene Zwei-Faktor-Authentifizierungscode war ungültig.", + "The provided two factor recovery code was invalid.": "Der angegebene Zwei-Faktor-Wiederherstellungscode war ungültig.", + "The response is not a streamed response.": "Die Antwort ist keine gestreamte Antwort.", + "The response is not a view.": "Die Antwort ist keine Ansicht.", + "The team name does not match.": "Der Teamname stimmt nicht überein.", + "Theme": "Farbschema", + "This action cannot be undone. This will permanently delete the team \":name\".": "Diese Aktion kann nicht rückgängig gemacht werden. Dadurch wird das Team „:name“ dauerhaft gelöscht.", + "This action is unauthorized.": "Diese Aktion ist nicht autorisiert.", + "This invitation has already been accepted.": "Diese Einladung wurde bereits angenommen.", + "This invitation has expired.": "Diese Einladung ist abgelaufen.", + "This invitation was sent to a different email address.": "Diese Einladung wurde an eine andere E-Mail-Adresse gesendet.", + "This is a secure area of the application. Please confirm your password before continuing.": "Dies ist ein sicherer Bereich der Anwendung. Bitte bestätigen Sie Ihr Passwort, bevor Sie fortfahren.", + "This password reset link will expire in :count minutes.": "Dieser Link zum Zurücksetzen des Passworts läuft in :count Minuten ab.", + "This team name is reserved and cannot be used.": "Dieser Teamname ist reserviert und kann nicht verwendet werden.", + "This user is already a member of the team.": "Dieser Benutzer ist bereits Mitglied des Teams.", + "to": "bis", + "To finish enabling two-factor authentication, scan the QR code or enter the setup key in your authenticator app.": "Um die Zwei-Faktor-Authentifizierung zu aktivieren, scannen Sie den QR-Code oder geben Sie den Einrichtungsschlüssel in Ihre Authentifizierungs-App ein.", + "Toggle navigation": "Navigation umschalten", + "Too Early": "Zu früh", + "Too Many Requests": "Zu viele Anfragen", + "Translate": "Übersetzen", + "Translate It": "Übersetze es", + "Two-factor authentication": "Zwei-Faktor-Authentifizierung", + "Two-factor authentication enabled": "Zwei-Faktor-Authentifizierung aktiviert", + "Two-factor authentication is now enabled. Scan the QR code or enter the setup key in your authenticator app.": "Die Zwei-Faktor-Authentifizierung ist nun aktiviert. Scannen Sie den QR-Code oder geben Sie den Setup-Schlüssel in Ihrer Authentifizierungs-App ein.", + "Type \":name\" to confirm": "Geben Sie \":name\" ein, um zu bestätigen", + "Unable to register passkey. Please try again.": "Passkey konnte nicht registriert werden. Bitte versuchen Sie es erneut.", + "Unable to register this passkey.": "Dieser Passkey kann nicht registriert werden.", + "Unable to sign in with this account.": "Anmeldung mit diesem Konto nicht möglich.", + "Unable to verify passkey. Please try again.": "Passkey konnte nicht verifiziert werden. Bitte versuchen Sie es erneut.", + "Unable to write the encrypted environment file.": "Die verschlüsselte Umgebungsdatei konnte nicht geschrieben werden.", + "Unauthorized": "Nicht autorisiert", + "Unavailable For Legal Reasons": "Aus rechtlichen Gründen nicht verfügbar", + "Unknown Error": "Unbekannter Fehler", + "Unpack": "Auspacken", + "Unprocessable Entity": "Unverarbeitbare Entität", + "Unsubscribe": "Abbestellen", + "Unsupported Media Type": "Nicht unterstützter Medientyp", + "Up": "Hoch", + "Update": "Aktualisieren", + "Update :name": ":name aktualisieren", + "Update password": "Passwort aktualisieren", + "Update the appearance settings for your account": "Aktualisieren Sie die Darstellungseinstellungen für Ihr Konto", + "Update your name and email address": "Aktualisieren Sie Ihren Namen und Ihre E-Mail-Adresse", + "Upgrade Required": "Upgrade erforderlich", + "URI Too Long": "URI ist zu lang", + "Use Proxy": "Proxy verwenden", + "User": "Benutzer", + "Variant Also Negotiates": "Variante verhandelt ebenfalls", + "Verify authentication code": "Authentifizierungscode überprüfen", + "Verify Email Address": "E-Mail-Adresse bestätigen", + "Verify your email address": "Bestätigen Sie Ihre E-Mail-Adresse", + "View": "Ansicht", + "View :name": ":name anzeigen", + "View recovery codes": "Wiederherstellungscodes anzeigen", + "View team": "Team anzeigen", + "Warning": "Warnung", + "Web Server is Down": "Webserver ist ausgefallen", + "Welcome": "Willkommen", + "When you enable two-factor authentication, you will be prompted for a secure pin during login. This pin can be retrieved from a TOTP-supported application on your phone.": "Wenn Sie die Zwei-Faktor-Authentifizierung aktivieren, werden Sie bei der Anmeldung zur Eingabe einer Sicherheits-PIN aufgefordert. Diese PIN können Sie über eine TOTP-unterstützte Anwendung auf Ihrem Smartphone abrufen.", + "Whoops!": "Ups!", + "Yes": "Ja", + "You are receiving this email because we received a password reset request for your account.": "Sie erhalten diese E-Mail, weil wir einen Antrag auf eine Zurücksetzung Ihres Passworts bekommen haben.", + "You don't belong to any teams yet.": "Sie gehören aktuell noch keinem Team an.", + "You left the team \":name\"": "Sie haben das Team „:name“ verlassen.", + "You will be prompted for a secure, random pin during login, which you can retrieve from the TOTP-supported application on your phone.": "Sie werden beim Anmelden zur Eingabe einer sicheren, zufällig generierten PIN aufgefordert, die Sie über die TOTP-fähige Anwendung auf Ihrem Smartphone abrufen können.", + "You've been invited to join :teamName": "Sie wurden zum Team „:teamName“ eingeladen.", + "Your email address is unverified.": "Ihre E-Mail-Adresse ist noch nicht bestätigt." +} diff --git a/lang/de/actions.php b/lang/de/actions.php new file mode 100644 index 0000000..e0776b6 --- /dev/null +++ b/lang/de/actions.php @@ -0,0 +1,119 @@ + 'Akzeptieren', + 'action' => 'Aktion', + 'actions' => 'Aktionen', + 'add' => 'Hinzufügen', + 'admin' => 'Administrator', + 'agree' => 'Zustimmen', + 'archive' => 'Archiv', + 'assign' => 'Zuordnen', + 'associate' => 'Assoziieren', + 'attach' => 'Anfügen', + 'browse' => 'Durchsuche', + 'cancel' => 'Stornieren', + 'choose' => 'Wählen', + 'choose_file' => 'Datei wählen', + 'choose_image' => 'Wählen Sie Bild', + 'click_to_copy' => 'Klicken Sie zum Kopieren', + 'close' => 'Schließen', + 'collapse' => 'Zusammenbruch', + 'collapse_all' => 'Alles reduzieren', + 'comment' => 'Kommentar', + 'confirm' => 'Bestätigen', + 'connect' => 'Verbinden', + 'create' => 'Erstellen', + 'delete' => 'Löschen', + 'detach' => 'Ablösen', + 'details' => 'Einzelheiten', + 'disable' => 'Deaktivieren', + 'discard' => 'Verwerfen', + 'done' => 'Erledigt', + 'down' => 'Runter', + 'duplicate' => 'Duplikat', + 'edit' => 'Bearbeiten', + 'enable' => 'Aktivieren', + 'expand' => 'Expandieren', + 'expand_all' => 'Alle erweitern', + 'explanation' => 'Erläuterung', + 'export' => 'Exportieren', + 'file' => 'Datei', + 'files' => 'Dateien', + 'go_home' => 'Nach Hause gehen', + 'hide' => 'Verstecken', + 'home' => 'Heim', + 'image' => 'Bild', + 'impersonate' => 'Imitieren', + 'impersonation' => 'Identitätswechsel', + 'import' => 'Importieren', + 'introduction' => 'Einführung', + 'like' => 'Wie', + 'load' => 'Belastung', + 'localize' => 'Lokalisieren', + 'log_in' => 'Anmeldung', + 'log_out' => 'Ausloggen', + 'named' => [ + 'add' => ':name hinzufügen', + 'choose' => ':name auswählen', + 'create' => ':name erstellen', + 'delete' => ':name löschen', + 'duplicate' => ':name duplizieren', + 'edit' => ':name bearbeiten', + 'export' => ':name exportieren', + 'hide' => ':name ausblenden', + 'import' => ':name importieren', + 'new' => 'Neu :name', + 'restore' => ':name wiederherstellen', + 'save' => ':name speichern', + 'search' => ':name suchen', + 'show' => ':name anzeigen', + 'update' => ':name aktualisieren', + 'view' => ':name anzeigen', + ], + 'new' => 'Neu', + 'no' => 'Nein', + 'open' => 'Öffen', + 'open_website' => 'Auf der Website öffnen', + 'preview' => 'Vorschau', + 'price' => 'Preis', + 'record' => 'Aufzeichnen', + 'restore' => 'Wiederherstellen', + 'save' => 'Speichern', + 'save_and_close' => 'Speichern und schließen', + 'save_and_return' => 'Speichern und zurückgehen', + 'search' => 'Suchen', + 'select' => 'Wählen', + 'select_all' => 'Alles auswählen', + 'send' => 'Absenden', + 'settings' => 'Einstellungen', + 'show' => 'Anzeigen', + 'show_all' => 'Alles anzeigen', + 'sign_in' => 'Anmelden', + 'solve' => 'Lösen', + 'start' => 'Starten', + 'stop' => 'Stoppen', + 'submit' => 'Absenden', + 'subscribe' => 'Abonnieren', + 'switch' => 'Umschalten', + 'switch_to_role' => 'Zur Rolle wechseln', + 'tag' => 'Schlagwort', + 'tags' => 'Stichworte', + 'target_link' => [ + 'blank' => 'In einem neuen Fenster öffnen', + 'parent' => 'In einem übergeordneten Frame öffnen', + 'self' => 'In einem aktuellen Fenster öffnen', + 'top' => 'Im obersten Rahmen öffnen', + ], + 'translate' => 'Übersetzen', + 'translate_it' => 'Übersetze es', + 'unpack' => 'Entpacken', + 'unsubscribe' => 'Abbestellen', + 'up' => 'Hoch', + 'update' => 'Aktualisieren', + 'user' => 'Benutzer', + 'view' => 'Ansicht', + 'yes' => 'Ja', +]; diff --git a/lang/de/auth.php b/lang/de/auth.php new file mode 100644 index 0000000..b9b93be --- /dev/null +++ b/lang/de/auth.php @@ -0,0 +1,9 @@ + 'Diese Kombination aus Zugangsdaten wurde nicht in unserer Datenbank gefunden.', + 'password' => 'Das Passwort ist falsch.', + 'throttle' => 'Zu viele Loginversuche. Versuchen Sie es bitte in :seconds Sekunden nochmal.', +]; diff --git a/lang/de/http-statuses.php b/lang/de/http-statuses.php new file mode 100644 index 0000000..58a68cf --- /dev/null +++ b/lang/de/http-statuses.php @@ -0,0 +1,84 @@ + 'Unbekannter Fehler', + '100' => 'Weiter', + '101' => 'Protokollwechsel', + '102' => 'In Bearbeitung', + '200' => 'OK', + '201' => 'Erstellt', + '202' => 'Akzeptiert', + '203' => 'Nicht verifizierte Information', + '204' => 'Kein Inhalt', + '205' => 'Inhalt zurücksetzen', + '206' => 'Teilinhalt', + '207' => 'Multistatus', + '208' => 'Bereits gemeldet', + '226' => 'IM verwendet', + '300' => 'Mehrere Auswahlmöglichkeiten', + '301' => 'Permanent verschoben', + '302' => 'Gefunden', + '303' => 'Siehe andere Seite', + '304' => 'Nicht modifiziert', + '305' => 'Proxy verwenden', + '307' => 'Temporäre Weiterleitung', + '308' => 'Permanente Weiterleitung', + '400' => 'Ungültige Anfrage', + '401' => 'Nicht autorisiert', + '402' => 'Zahlung erforderlich', + '403' => 'Verboten', + '404' => 'Nicht gefunden', + '405' => 'Methode nicht erlaubt', + '406' => 'Nicht annehmbar', + '407' => 'Proxy-Authentifizierung erforderlich', + '408' => 'Zeitüberschreitung der Anfrage', + '409' => 'Konflikt', + '410' => 'Nicht mehr verfügbar', + '411' => 'Länge erforderlich', + '412' => 'Vorbedingung fehlgeschlagen', + '413' => 'Nutzlast zu groß', + '414' => 'URI zu lang', + '415' => 'Nicht unterstützter Medientyp', + '416' => 'Bereich nicht erfüllbar', + '417' => 'Erwartung gescheitert', + '418' => 'Ich bin eine Teekanne', + '419' => 'Sitzung ist abgelaufen', + '421' => 'Fehlgeleitete Anfrage', + '422' => 'Unverarbeitbare Entität', + '423' => 'Gesperrt', + '424' => 'Fehlgeschlagene Abhängigkeit', + '425' => 'Zu früh', + '426' => 'Upgrade erforderlich', + '428' => 'Voraussetzung erforderlich', + '429' => 'Zu viele Anfragen', + '431' => 'Anfrage-Header-Felder zu groß', + '444' => 'Verbindung ohne Antwort geschlossen', + '449' => 'Wiederhole mit', + '451' => 'Aus rechtlichen Gründen nicht verfügbar', + '499' => 'Client-Closed-Request', + '500' => 'Interner Serverfehler', + '501' => 'Nicht implementiert', + '502' => 'Fehlerhaftes Gateway', + '503' => 'Wartungsmodus', + '504' => 'Gateway-Zeitüberschreitung', + '505' => 'HTTP Version nicht unterstützt', + '506' => 'Variante verhandelt auch', + '507' => 'Nicht genügend Speicherplatz', + '508' => 'Endlosschleife erkannt', + '509' => 'Bandbreitenlimit überschritten', + '510' => 'Nicht erweitert', + '511' => 'Netzwerkauthentifizierung erforderlich', + '520' => 'Unbekannter Fehler', + '521' => 'Webserver ist ausgefallen', + '522' => 'Verbindung abgelaufen', + '523' => 'Quelle ist nicht erreichbar', + '524' => 'Eine Zeitüberschreitung ist aufgetreten', + '525' => 'SSL Handshake fehlgeschlagen', + '526' => 'Ungültiges SSL-Zertifikat', + '527' => 'Railgun-Fehler', + '598' => 'Zeitüberschreitungsfehler beim Lesen des Netzwerks', + '599' => 'Zeitüberschreitungsfehler bei Netzwerkverbindung', + 'unknownError' => 'Unbekannter Fehler', +]; diff --git a/lang/de/pagination.php b/lang/de/pagination.php new file mode 100644 index 0000000..c912b5d --- /dev/null +++ b/lang/de/pagination.php @@ -0,0 +1,8 @@ + 'Weiter »', + 'previous' => '« Zurück', +]; diff --git a/lang/de/passwords.php b/lang/de/passwords.php new file mode 100644 index 0000000..9634bdd --- /dev/null +++ b/lang/de/passwords.php @@ -0,0 +1,11 @@ + 'Das Passwort wurde zurückgesetzt!', + 'sent' => 'E-Mail zum Zurücksetzen des Passworts wurde gesendet!', + 'throttled' => 'Bitte warten Sie, bevor Sie es erneut versuchen.', + 'token' => 'Der Passwort-Wiederherstellungsschlüssel ist ungültig oder abgelaufen.', + 'user' => 'Es konnte leider kein Nutzer mit dieser E-Mail-Adresse gefunden werden.', +]; diff --git a/lang/de/validation.php b/lang/de/validation.php new file mode 100644 index 0000000..639de22 --- /dev/null +++ b/lang/de/validation.php @@ -0,0 +1,290 @@ + ':Attribute muss akzeptiert werden.', + 'accepted_if' => ':Attribute muss akzeptiert werden, wenn :other :value ist.', + 'active_url' => ':Attribute ist keine gültige Internet-Adresse.', + 'after' => ':Attribute muss ein Datum nach :date sein.', + 'after_or_equal' => ':Attribute muss ein Datum nach :date oder gleich :date sein.', + 'alpha' => ':Attribute darf nur aus Buchstaben bestehen.', + 'alpha_dash' => ':Attribute darf nur aus Buchstaben, Zahlen, Binde- und Unterstrichen bestehen.', + 'alpha_num' => ':Attribute darf nur aus Buchstaben und Zahlen bestehen.', + 'any_of' => ':Attribute ist ungültig.', + 'array' => ':Attribute muss ein Array sein.', + 'array_keys' => 'Das Feld :Attribute darf nur die folgenden Schlüssel enthalten: :values.', + 'ascii' => 'Die :attribute darf nur alphanumerische Single-Byte-Zeichen und -Symbole enthalten.', + 'base64' => 'Das Feld :attribute muss eine gültige Base64-Zeichenfolge sein.', + 'before' => ':Attribute muss ein Datum vor :date sein.', + 'before_or_equal' => ':Attribute muss ein Datum vor :date oder gleich :date sein.', + 'between' => [ + 'array' => ':Attribute muss zwischen :min & :max Elemente haben.', + 'file' => ':Attribute muss zwischen :min & :max Kilobytes groß sein.', + 'numeric' => ':Attribute muss zwischen :min & :max liegen.', + 'string' => ':Attribute muss zwischen :min & :max Zeichen lang sein.', + ], + 'boolean' => ':Attribute muss entweder \'true\' oder \'false\' sein.', + 'can' => 'Das Feld :attribute enthält einen nicht autorisierten Wert.', + 'confirmed' => ':Attribute stimmt nicht mit der Bestätigung überein.', + 'contains' => 'Dem Feld :attribute fehlt ein erforderlicher Wert.', + 'current_password' => 'Das Passwort ist falsch.', + 'date' => ':Attribute muss ein gültiges Datum sein.', + 'date_equals' => ':Attribute muss ein Datum gleich :date sein.', + 'date_format' => ':Attribute entspricht nicht dem gültigen Format für :format.', + 'decimal' => 'Die :attribute muss :decimal Dezimalstellen haben.', + 'declined' => ':Attribute muss abgelehnt werden.', + 'declined_if' => ':Attribute muss abgelehnt werden wenn :other :value ist.', + 'different' => ':Attribute und :other müssen sich unterscheiden.', + 'digits' => ':Attribute muss :digits Stellen haben.', + 'digits_between' => ':Attribute muss zwischen :min und :max Stellen haben.', + 'dimensions' => ':Attribute hat ungültige Bildabmessungen.', + 'distinct' => ':Attribute beinhaltet einen bereits vorhandenen Wert.', + 'doesnt_contain' => ':Attribute darf keines der folgenden enthalten: :values.', + 'doesnt_end_with' => ':Attribute darf nicht mit einem der folgenden enden: :values.', + 'doesnt_start_with' => ':Attribute darf nicht mit einem der folgenden beginnen: :values.', + 'email' => ':Attribute muss eine gültige E-Mail-Adresse sein.', + 'encoding' => ':Attribute muss als :encoding kodiert sein.', + 'ends_with' => ':Attribute muss eine der folgenden Endungen aufweisen: :values', + 'enum' => 'Der ausgewählte Wert ist ungültig.', + 'exists' => 'Der gewählte Wert für :attribute ist ungültig.', + 'extensions' => 'Das Feld :attribute muss eine der folgenden Erweiterungen haben: :values.', + 'file' => ':Attribute muss eine Datei sein.', + 'filled' => ':Attribute muss ausgefüllt sein.', + 'gt' => [ + 'array' => ':Attribute muss mehr als :value Elemente haben.', + 'file' => ':Attribute muss größer als :value Kilobytes sein.', + 'numeric' => ':Attribute muss größer als :value sein.', + 'string' => ':Attribute muss länger als :value Zeichen sein.', + ], + 'gte' => [ + 'array' => ':Attribute muss mindestens :value Elemente haben.', + 'file' => ':Attribute muss größer oder gleich :value Kilobytes sein.', + 'numeric' => ':Attribute muss größer oder gleich :value sein.', + 'string' => ':Attribute muss mindestens :value Zeichen lang sein.', + ], + 'hex_color' => 'Das Feld :attribute muss eine gültige Hexadezimalfarbe sein.', + 'image' => ':Attribute muss ein Bild sein.', + 'in' => 'Der gewählte Wert für :attribute ist ungültig.', + 'in_array' => 'Der gewählte Wert für :attribute kommt nicht in :other vor.', + 'in_array_keys' => 'Das Feld :Attribute muss mindestens einen der folgenden Schlüssel enthalten: :Values.', + 'integer' => ':Attribute muss eine ganze Zahl sein.', + 'ip' => ':Attribute muss eine gültige IP-Adresse sein.', + 'ipv4' => ':Attribute muss eine gültige IPv4-Adresse sein.', + 'ipv6' => ':Attribute muss eine gültige IPv6-Adresse sein.', + 'json' => ':Attribute muss ein gültiger JSON-String sein.', + 'list' => ':Attribute muss eine Liste sein.', + 'lowercase' => ':Attribute muss in Kleinbuchstaben sein.', + 'lt' => [ + 'array' => ':Attribute muss weniger als :value Elemente haben.', + 'file' => ':Attribute muss kleiner als :value Kilobytes sein.', + 'numeric' => ':Attribute muss kleiner als :value sein.', + 'string' => ':Attribute muss kürzer als :value Zeichen sein.', + ], + 'lte' => [ + 'array' => ':Attribute darf maximal :value Elemente haben.', + 'file' => ':Attribute muss kleiner oder gleich :value Kilobytes sein.', + 'numeric' => ':Attribute muss kleiner oder gleich :value sein.', + 'string' => ':Attribute darf maximal :value Zeichen lang sein.', + ], + 'mac_address' => 'Der Wert muss eine gültige MAC-Adresse sein.', + 'max' => [ + 'array' => ':Attribute darf maximal :max Elemente haben.', + 'file' => ':Attribute darf maximal :max Kilobytes groß sein.', + 'numeric' => ':Attribute darf maximal :max sein.', + 'string' => ':Attribute darf maximal :max Zeichen haben.', + ], + 'max_digits' => ':Attribute darf maximal :max Ziffern lang sein.', + 'mimes' => ':Attribute muss den Dateityp :values haben.', + 'mimetypes' => ':Attribute muss den Dateityp :values haben.', + 'min' => [ + 'array' => ':Attribute muss mindestens :min Elemente haben.', + 'file' => ':Attribute muss mindestens :min Kilobytes groß sein.', + 'numeric' => ':Attribute muss mindestens :min sein.', + 'string' => ':Attribute muss mindestens :min Zeichen lang sein.', + ], + 'min_digits' => ':Attribute muss mindestens :min Ziffern lang sein.', + 'missing' => 'Das Feld :attribute muss fehlen.', + 'missing_if' => 'Das Feld :attribute muss fehlen, wenn :other gleich :value ist.', + 'missing_unless' => 'Das Feld :attribute muss fehlen, es sei denn, :other ist :value.', + 'missing_with' => 'Das Feld :attribute muss fehlen, wenn :values vorhanden ist.', + 'missing_with_all' => 'Das Feld :attribute muss fehlen, wenn :values vorhanden sind.', + 'multiple_of' => ':Attribute muss ein Vielfaches von :value sein.', + 'not_in' => 'Der gewählte Wert für :attribute ist ungültig.', + 'not_regex' => ':Attribute hat ein ungültiges Format.', + 'numeric' => ':Attribute muss eine Zahl sein.', + 'password' => [ + 'letters' => ':Attribute muss mindestens einen Buchstaben beinhalten.', + 'mixed' => ':Attribute muss mindestens einen Großbuchstaben und einen Kleinbuchstaben beinhalten.', + 'numbers' => ':Attribute muss mindestens eine Zahl beinhalten.', + 'symbols' => ':Attribute muss mindestens ein Sonderzeichen beinhalten.', + 'uncompromised' => ':Attribute wurde in einem Datenleck gefunden. Bitte wählen Sie ein anderes :attribute.', + ], + 'present' => ':Attribute muss vorhanden sein.', + 'present_if' => 'Das Feld :attribute muss vorhanden sein, wenn :other gleich :value ist.', + 'present_unless' => 'Das Feld :attribute muss vorhanden sein, es sei denn, :other ist :value.', + 'present_with' => 'Das Feld :attribute muss vorhanden sein, wenn :values vorhanden ist.', + 'present_with_all' => 'Das Feld :attribute muss vorhanden sein, wenn :values vorhanden sind.', + 'prohibited' => ':Attribute ist unzulässig.', + 'prohibited_if' => ':Attribute ist unzulässig, wenn :other :value ist.', + 'prohibited_if_accepted' => 'Das Feld :attribute ist unzulässig, wenn :other gewählt ist.', + 'prohibited_if_declined' => 'Das Feld :attribute ist unzulässig, wenn :other abgelehnt ist.', + 'prohibited_unless' => ':Attribute ist unzulässig, wenn :other nicht :values ist.', + 'prohibits' => ':Attribute verbietet die Angabe von :other.', + 'regex' => ':Attribute Format ist ungültig.', + 'required' => ':Attribute muss ausgefüllt werden.', + 'required_array_keys' => 'Dieses Feld muss Einträge enthalten für: :values.', + 'required_if' => ':Attribute muss ausgefüllt werden, wenn :other den Wert :value hat.', + 'required_if_accepted' => ':Attribute muss ausgefüllt werden, wenn :other gewählt ist.', + 'required_if_declined' => 'Das Feld :attribute ist erforderlich, wenn :other abgelehnt wird.', + 'required_unless' => ':Attribute muss ausgefüllt werden, wenn :other nicht den Wert :values hat.', + 'required_with' => ':Attribute muss ausgefüllt werden, wenn :values ausgefüllt wurde.', + 'required_with_all' => ':Attribute muss ausgefüllt werden, wenn :values ausgefüllt wurde.', + 'required_without' => ':Attribute muss ausgefüllt werden, wenn :values nicht ausgefüllt wurde.', + 'required_without_all' => ':Attribute muss ausgefüllt werden, wenn keines der Felder :values ausgefüllt wurde.', + 'same' => ':Attribute und :other müssen übereinstimmen.', + 'size' => [ + 'array' => ':Attribute muss genau :size Elemente haben.', + 'file' => ':Attribute muss :size Kilobyte groß sein.', + 'numeric' => ':Attribute muss gleich :size sein.', + 'string' => ':Attribute muss :size Zeichen lang sein.', + ], + 'starts_with' => ':Attribute muss mit einem der folgenden Anfänge aufweisen: :values', + 'string' => ':Attribute muss ein String sein.', + 'timezone' => ':Attribute muss eine gültige Zeitzone sein.', + 'ulid' => 'Die :attribute muss eine gültige ULID sein.', + 'unique' => ':Attribute ist bereits vergeben.', + 'uploaded' => ':Attribute konnte nicht hochgeladen werden.', + 'uppercase' => ':Attribute muss in Großbuchstaben sein.', + 'url' => ':Attribute muss eine URL sein.', + 'uuid' => ':Attribute muss ein UUID sein.', + 'attributes' => [ + 'address' => 'Adresse', + 'affiliate_url' => 'Affiliate-URL', + 'age' => 'Alter', + 'amount' => 'Höhe', + 'announcement' => 'Bekanntmachung', + 'area' => 'Gebiet', + 'audience_prize' => 'Publikumspreis', + 'audience_winner' => 'Publikumsgewinner', + 'available' => 'Verfügbar', + 'birthday' => 'Geburtstag', + 'body' => 'Körper', + 'city' => 'Stadt', + 'color' => 'Farbe', + 'company' => 'Unternehmen', + 'compilation' => 'Zusammenstellung', + 'concept' => 'Konzept', + 'conditions' => 'Bedingungen', + 'content' => 'Inhalt', + 'contest' => 'Wettbewerb', + 'country' => 'Land', + 'cover' => 'Abdeckung', + 'created_at' => 'Erstellt am', + 'creator' => 'Ersteller', + 'currency' => 'Währung', + 'current_password' => 'Derzeitiges Passwort', + 'customer' => 'Kunde', + 'date' => 'Datum', + 'date_of_birth' => 'Geburtsdatum', + 'dates' => 'Termine', + 'day' => 'Tag', + 'deleted_at' => 'Gelöscht am', + 'description' => 'Beschreibung', + 'display_type' => 'Anzeigetyp', + 'district' => 'Bezirk', + 'duration' => 'Dauer', + 'email' => 'E-Mail-Adresse', + 'excerpt' => 'Auszug', + 'filter' => 'Filter', + 'finished_at' => 'fertig um', + 'first_name' => 'Vorname', + 'gender' => 'Geschlecht', + 'grand_prize' => 'Hauptpreis', + 'group' => 'Gruppe', + 'hour' => 'Stunde', + 'image' => 'Bild', + 'image_desktop' => 'Desktop-Bild', + 'image_main' => 'Hauptbild', + 'image_mobile' => 'mobiles Bild', + 'images' => 'Bilder', + 'is_audience_winner' => 'ist Publikumssieger', + 'is_hidden' => 'ist versteckt', + 'is_subscribed' => 'ist abonniert', + 'is_visible' => 'ist sichtbar', + 'is_winner' => 'ist Gewinner', + 'items' => 'Artikel', + 'key' => 'Schlüssel', + 'last_name' => 'Nachname', + 'lesson' => 'Lektion', + 'line_address_1' => 'Adresszeile 1', + 'line_address_2' => 'Adresszeile 2', + 'login' => 'Anmeldung', + 'message' => 'Nachricht', + 'middle_name' => 'Zweitname', + 'minute' => 'Minute', + 'mobile' => 'Handynummer', + 'month' => 'Monat', + 'name' => 'Name', + 'national_code' => 'Länderkennung', + 'number' => 'Nummer', + 'password' => 'Passwort', + 'password_confirmation' => 'Passwortbestätigung', + 'phone' => 'Telefonnummer', + 'photo' => 'Foto', + 'portfolio' => 'Portfolio', + 'postal_code' => 'Postleitzahl', + 'preview' => 'Vorschau', + 'price' => 'Preis', + 'product_id' => 'Produkt ID', + 'product_uid' => 'Produkt-UID', + 'product_uuid' => 'Produkt-UUID', + 'promo_code' => 'Aktionscode', + 'province' => 'Provinz', + 'quantity' => 'Menge', + 'reason' => 'Grund', + 'recaptcha_response_field' => 'Captcha-Feld', + 'referee' => 'Schiedsrichter', + 'referees' => 'Schiedsrichter', + 'region' => 'Region', + 'reject_reason' => 'Ablehnungsgrund', + 'remember' => 'Erinnern', + 'restored_at' => 'Wiederhergestellt am', + 'result_text_under_image' => 'Ergebnistext unter Bild', + 'role' => 'Rolle', + 'rule' => 'Regel', + 'rules' => 'Regeln', + 'second' => 'Sekunde', + 'sex' => 'Geschlecht', + 'shipment' => 'Sendung', + 'short_text' => 'Kurzer Text', + 'size' => 'Größe', + 'skills' => 'Fähigkeiten', + 'slug' => 'Schnecke', + 'specialization' => 'Spezialisierung', + 'started_at' => 'fing an bei', + 'state' => 'Bundesland', + 'status' => 'Status', + 'street' => 'Straße', + 'student' => 'Schüler/Student', + 'subject' => 'Gegenstand', + 'tag' => 'Etikett', + 'tags' => 'Stichworte', + 'teacher' => 'Lehrer', + 'terms' => 'Bedingungen', + 'test_description' => 'Test Beschreibung', + 'test_locale' => 'Test Region', + 'test_name' => 'Testname', + 'text' => 'Text', + 'time' => 'Uhrzeit', + 'title' => 'Titel', + 'type' => 'Typ', + 'updated_at' => 'Aktualisiert am', + 'user' => 'Benutzer', + 'username' => 'Benutzername', + 'value' => 'Wert', + 'winner' => 'Gewinner', + 'work' => 'Arbeit', + 'year' => 'Jahr', + ], +]; diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..8f74242 --- /dev/null +++ b/lang/en.json @@ -0,0 +1,424 @@ +{ + "(and :count more error)": "(and :count more error)", + "(and :count more errors)": "(and :count more error)|(and :count more errors)|(and :count more errors)", + "2FA recovery codes": "2FA recovery codes", + ":action to join the \":team\" team.": ":action to join the \":team\" team.", + ":inviter invited you to join this team.": ":inviter invited you to join this team.", + ":inviterName has invited you to join the :teamName team.": ":inviterName has invited you to join the :teamName team.", + "A decryption key is required.": "A decryption key is required.", + "A new verification link has been sent to the email address you provided during registration.": "A new verification link has been sent to the email address you provided during registration.", + "A new verification link has been sent to your email address.": "A new verification link has been sent to your email address.", + "A Timeout Occurred": "A Timeout Occurred", + "Accept": "Accept", + "Accept or decline the teams you have been invited to join.": "Accept or decline the teams you have been invited to join.", + "Accepted": "Accepted", + "Action": "Action", + "Actions": "Actions", + "Add": "Add", + "Add :name": "Add :name", + "Add a passkey to sign in without a password": "Add a passkey to sign in without a password", + "Add passkey": "Add passkey", + "Added :time": "Added :time", + "Admin": "Admin", + "Agree": "Agree", + "All rights reserved.": "All rights reserved.", + "Already have an account?": "Already have an account?", + "Already Reported": "Already Reported", + "An invitation has already been sent to this email address.": "An invitation has already been sent to this email address.", + "Appearance": "Appearance", + "Appearance settings": "Appearance settings", + "Archive": "Archive", + "Are you sure you want to cancel the invitation for :email?": "Are you sure you want to cancel the invitation for :email?", + "Are you sure you want to delete your account?": "Are you sure you want to delete your account?", + "Are you sure you want to leave :name?": "Are you sure you want to leave :name?", + "Are you sure you want to remove :name from this team?": "Are you sure you want to remove :name from this team?", + "Are you sure you want to remove the passkey \":name\"? You will no longer be able to use it to sign in.": "Are you sure you want to remove the passkey \":name\"? You will no longer be able to use it to sign in.", + "Are you sure?": "Are you sure?", + "Assign": "Assign", + "Associate": "Associate", + "Attach": "Attach", + "Authenticating...": "Authenticating...", + "Authentication code": "Authentication code", + "Back": "Back", + "Bad Gateway": "Bad Gateway", + "Bad Request": "Bad Request", + "Bandwidth Limit Exceeded": "Bandwidth Limit Exceeded", + "Browse": "Browse", + "Cancel": "Cancel", + "Cancel invitation": "Cancel invitation", + "Choose": "Choose", + "Choose :name": "Choose :name", + "Choose File": "Choose File", + "Choose Image": "Choose Image", + "Click here to re-send the verification email.": "Click here to re-send the verification email.", + "Click to copy": "Click to copy", + "Client Closed Request": "Client Closed Request", + "Close": "Close", + "Collapse": "Collapse", + "Collapse All": "Collapse All", + "Comment": "Comment", + "Confirm": "Confirm", + "Confirm password": "Confirm password", + "Confirm with passkey": "Confirm with passkey", + "Confirming...": "Confirming...", + "Conflict": "Conflict", + "Connect": "Connect", + "Connection Closed Without Response": "Connection Closed Without Response", + "Connection Timed Out": "Connection Timed Out", + "Continue": "Continue", + "Create": "Create", + "Create :name": "Create :name", + "Create a new team": "Create a new team", + "Create account": "Create account", + "Create an account": "Create an account", + "Create team": "Create team", + "Created": "Created", + "Current password": "Current password", + "Dark": "Dark", + "Dashboard": "Dashboard", + "Decline": "Decline", + "Delete": "Delete", + "Delete :name": "Delete :name", + "Delete account": "Delete account", + "Delete team": "Delete team", + "Delete your account and all of its resources": "Delete your account and all of its resources", + "Detach": "Detach", + "Details": "Details", + "Disable": "Disable", + "Disable 2FA": "Disable 2FA", + "Discard": "Discard", + "Documentation": "Documentation", + "Don't have an account?": "Don't have an account?", + "Done": "Done", + "Down": "Down", + "Duplicate": "Duplicate", + "Duplicate :name": "Duplicate :name", + "e.g., MacBook Pro, iPhone": "e.g., MacBook Pro, iPhone", + "Each recovery code can be used once to access your account and will be removed after use. If you need more, click Regenerate codes above.": "Each recovery code can be used once to access your account and will be removed after use. If you need more, click Regenerate codes above.", + "Edit": "Edit", + "Edit :name": "Edit :name", + "Edit team": "Edit team", + "Email": "Email", + "Email address": "Email address", + "Email password reset link": "Email password reset link", + "Email verification": "Email verification", + "Enable": "Enable", + "Enable 2FA": "Enable 2FA", + "Enable two-factor authentication": "Enable two-factor authentication", + "Encrypted environment file already exists.": "Encrypted environment file already exists.", + "Encrypted environment file not found.": "Encrypted environment file not found.", + "Ensure your account is using a long, random password to stay secure": "Ensure your account is using a long, random password to stay secure", + "Enter the 6-digit code from your authenticator app.": "Enter the 6-digit code from your authenticator app.", + "Enter the authentication code provided by your authenticator application.": "Enter the authentication code provided by your authenticator application.", + "Enter your details below to create your account": "Enter your details below to create your account", + "Enter your email and password below to log in": "Enter your email and password below to log in", + "Enter your email to receive a password reset link": "Enter your email to receive a password reset link", + "Environment file already exists.": "Environment file already exists.", + "Environment file not found.": "Environment file not found.", + "errors": "errors", + "Expand": "Expand", + "Expand All": "Expand All", + "Expectation Failed": "Expectation Failed", + "Explanation": "Explanation", + "Export": "Export", + "Export :name": "Export :name", + "Failed Dependency": "Failed Dependency", + "File": "File", + "Files": "Files", + "Forbidden": "Forbidden", + "Forgot password": "Forgot password", + "Forgot your password?": "Forgot your password?", + "Found": "Found", + "Full name": "Full name", + "Gateway Timeout": "Gateway Timeout", + "Give this passkey a name to help you identify it later.": "Give this passkey a name to help you identify it later.", + "Give your team a name to get started.": "Give your team a name to get started.", + "Go Home": "Go Home", + "Go to page :page": "Go to page :page", + "Gone": "Gone", + "Hello!": "Hello!", + "Hide": "Hide", + "Hide :name": "Hide :name", + "Hide recovery codes": "Hide recovery codes", + "Home": "Home", + "HTTP Version Not Supported": "HTTP Version Not Supported", + "I'm a teapot": "I'm a teapot", + "If you did not create an account, no further action is required.": "If you did not create an account, no further action is required.", + "If you did not request a password reset, no further action is required.": "If you did not request a password reset, no further action is required.", + "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:", + "IM Used": "IM Used", + "Image": "Image", + "Impersonate": "Impersonate", + "Impersonation": "Impersonation", + "Import": "Import", + "Import :name": "Import :name", + "Insufficient Storage": "Insufficient Storage", + "Internal Server Error": "Internal Server Error", + "Introduction": "Introduction", + "Invalid credential format.": "Invalid credential format.", + "Invalid filename.": "Invalid filename.", + "Invalid JSON was returned from the route.": "Invalid JSON was returned from the route.", + "Invalid SSL Certificate": "Invalid SSL Certificate", + "Invitation accepted.": "Invitation accepted.", + "Invitation cancelled.": "Invitation cancelled.", + "Invitation declined.": "Invitation declined.", + "Invitation sent.": "Invitation sent.", + "Invitations that have not been accepted yet": "Invitations that have not been accepted yet", + "Invite a team member": "Invite a team member", + "Invite member": "Invite member", + "Keep invitation": "Keep invitation", + "Last used :time": "Last used :time", + "Leave team": "Leave team", + "Length Required": "Length Required", + "Light": "Light", + "Like": "Like", + "Load": "Load", + "Localize": "Localize", + "Location": "Location", + "Locked": "Locked", + "Log In": "Log In", + "Log in": "Log in", + "log in": "log in", + "Log in and visit your dashboard to accept or decline this invitation.": "Log in and visit your dashboard to accept or decline this invitation.", + "Log in to your account": "Log in to your account", + "Log Out": "Log Out", + "Log out": "Log out", + "Login": "Login", + "login using a recovery code": "login using a recovery code", + "login using an authentication code": "login using an authentication code", + "Logout": "Logout", + "Loop Detected": "Loop Detected", + "Maintenance Mode": "Maintenance Mode", + "Manage who belongs to this team": "Manage who belongs to this team", + "Manage your passkeys for passwordless sign-in": "Manage your passkeys for passwordless sign-in", + "Manage your profile and account settings": "Manage your profile and account settings", + "Manage your team settings": "Manage your team settings", + "Manage your teams and team memberships": "Manage your teams and team memberships", + "Manage your two-factor authentication settings": "Manage your two-factor authentication settings", + "Member removed.": "Member removed.", + "Member role updated.": "Member role updated.", + "Method Not Allowed": "Method Not Allowed", + "Misdirected Request": "Misdirected Request", + "Moved Permanently": "Moved Permanently", + "Multi-Status": "Multi-Status", + "Multiple Choices": "Multiple Choices", + "Name": "Name", + "Network Authentication Required": "Network Authentication Required", + "Network Connect Timeout Error": "Network Connect Timeout Error", + "Network Read Timeout Error": "Network Read Timeout Error", + "New": "New", + "New :name": "New :name", + "New password": "New password", + "New team": "New team", + "No": "No", + "No Content": "No Content", + "No passkeys yet": "No passkeys yet", + "Non-Authoritative Information": "Non-Authoritative Information", + "Not Acceptable": "Not Acceptable", + "Not Extended": "Not Extended", + "Not Found": "Not Found", + "Not Implemented": "Not Implemented", + "Not Modified": "Not Modified", + "of": "of", + "OK": "OK", + "Once your account is deleted, all of its resources and data will also be permanently deleted. Please confirm you would like to permanently delete your account.": "Once your account is deleted, all of its resources and data will also be permanently deleted. Please confirm you would like to permanently delete your account.", + "Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.": "Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.", + "Open": "Open", + "Open in a current window": "Open in a current window", + "Open in a new window": "Open in a new window", + "Open in a parent frame": "Open in a parent frame", + "Open in the topmost frame": "Open in the topmost frame", + "Open on the website": "Open on the website", + "Or confirm with password": "Or confirm with password", + "Or continue with email": "Or continue with email", + "or you can": "or you can", + "or, enter the code manually": "or, enter the code manually", + "Or, return to": "Or, return to", + "Origin Is Unreachable": "Origin Is Unreachable", + "Page Expired": "Page Expired", + "Pagination Navigation": "Pagination Navigation", + "Partial Content": "Partial Content", + "Passkey name": "Passkey name", + "Passkey not recognized. It may have been removed from your account.": "Passkey not recognized. It may have been removed from your account.", + "Passkey registration session expired. Please try again.": "Passkey registration session expired. Please try again.", + "Passkey verification session expired. Please try again.": "Passkey verification session expired. Please try again.", + "Passkeys": "Passkeys", + "Passkeys are not supported in this browser.": "Passkeys are not supported in this browser.", + "Password": "Password", + "Password updated.": "Password updated.", + "Payload Too Large": "Payload Too Large", + "Payment Required": "Payment Required", + "Pending invitations": "Pending invitations", + "Pending team invitations": "Pending team invitations", + "Permanent Redirect": "Permanent Redirect", + "Permanently delete your team": "Permanently delete your team", + "Personal": "Personal", + "Platform": "Platform", + "Please click the button below to verify your email address.": "Please click the button below to verify your email address.", + "Please confirm access to your account by entering one of your emergency recovery codes.": "Please confirm access to your account by entering one of your emergency recovery codes.", + "Please enter your new password below": "Please enter your new password below", + "Please proceed with caution, this cannot be undone.": "Please proceed with caution, this cannot be undone.", + "Please verify your email address by clicking on the link we just emailed to you.": "Please verify your email address by clicking on the link we just emailed to you.", + "Precondition Failed": "Precondition Failed", + "Precondition Required": "Precondition Required", + "Preview": "Preview", + "Price": "Price", + "Processing": "Processing", + "Profile": "Profile", + "Profile settings": "Profile settings", + "Profile updated.": "Profile updated.", + "Proxy Authentication Required": "Proxy Authentication Required", + "Railgun Error": "Railgun Error", + "Range Not Satisfiable": "Range Not Satisfiable", + "Record": "Record", + "Recovery code": "Recovery code", + "Recovery codes": "Recovery codes", + "Recovery codes let you regain access if you lose your 2FA device. Store them in a secure password manager.": "Recovery codes let you regain access if you lose your 2FA device. Store them in a secure password manager.", + "Regards,": "Regards,", + "Regenerate codes": "Regenerate codes", + "Register": "Register", + "Register passkey": "Register passkey", + "Registering...": "Registering...", + "Remember me": "Remember me", + "Remove member": "Remove member", + "Remove passkey": "Remove passkey", + "Remove team member": "Remove team member", + "Repository": "Repository", + "Request Header Fields Too Large": "Request Header Fields Too Large", + "Request Timeout": "Request Timeout", + "Resend verification email": "Resend verification email", + "Reset Content": "Reset Content", + "Reset Password": "Reset Password", + "Reset password": "Reset password", + "Reset your password": "Reset your password", + "Restore": "Restore", + "Restore :name": "Restore :name", + "results": "results", + "Retry With": "Retry With", + "Role": "Role", + "Save": "Save", + "Save & Close": "Save & Close", + "Save & Return": "Save & Return", + "Save :name": "Save :name", + "Search": "Search", + "Search :name": "Search :name", + "Security": "Security", + "Security settings": "Security settings", + "See Other": "See Other", + "Select": "Select", + "Select All": "Select All", + "Select team": "Select team", + "Send": "Send", + "Send an invitation to join this team.": "Send an invitation to join this team.", + "Send invitation": "Send invitation", + "Server Error": "Server Error", + "Service Unavailable": "Service Unavailable", + "Session Has Expired": "Session Has Expired", + "Settings": "Settings", + "Show": "Show", + "Show :name": "Show :name", + "Show All": "Show All", + "Showing": "Showing", + "Sign In": "Sign In", + "Sign in with a passkey": "Sign in with a passkey", + "Sign up": "Sign up", + "Solve": "Solve", + "SSL Handshake Failed": "SSL Handshake Failed", + "Start": "Start", + "Stop": "Stop", + "Submit": "Submit", + "Subscribe": "Subscribe", + "Switch": "Switch", + "Switch To Role": "Switch To Role", + "Switching Protocols": "Switching Protocols", + "System": "System", + "Tag": "Tag", + "Tags": "Tags", + "Team created.": "Team created.", + "Team deleted.": "Team deleted.", + "Team members": "Team members", + "Team name": "Team name", + "Team updated.": "Team updated.", + "Teams": "Teams", + "Temporary Redirect": "Temporary Redirect", + "The :attribute must be at least :length characters and contain at least one number.": "The :attribute must be at least :length characters and contain at least one number.", + "The :attribute must be at least :length characters and contain at least one special character and one number.": "The :attribute must be at least :length characters and contain at least one special character and one number.", + "The :attribute must be at least :length characters and contain at least one special character.": "The :attribute must be at least :length characters and contain at least one special character.", + "The :attribute must be at least :length characters and contain at least one uppercase character and one number.": "The :attribute must be at least :length characters and contain at least one uppercase character and one number.", + "The :attribute must be at least :length characters and contain at least one uppercase character and one special character.": "The :attribute must be at least :length characters and contain at least one uppercase character and one special character.", + "The :attribute must be at least :length characters and contain at least one uppercase character, one number, and one special character.": "The :attribute must be at least :length characters and contain at least one uppercase character, one number, and one special character.", + "The :attribute must be at least :length characters and contain at least one uppercase character.": "The :attribute must be at least :length characters and contain at least one uppercase character.", + "The :attribute must be at least :length characters.": "The :attribute must be at least :length characters.", + "The existing encrypted environment file is not in readable format. Use --force to overwrite it.": "The existing encrypted environment file is not in readable format. Use --force to overwrite it.", + "The existing encryption key is required to update the encrypted environment file.": "The existing encryption key is required to update the encrypted environment file.", + "The given data was invalid.": "The given data was invalid.", + "The provided password does not match your current password.": "The provided password does not match your current password.", + "The provided password was incorrect.": "The provided password was incorrect.", + "The provided two factor authentication code was invalid.": "The provided two factor authentication code was invalid.", + "The provided two factor recovery code was invalid.": "The provided two factor recovery code was invalid.", + "The response is not a streamed response.": "The response is not a streamed response.", + "The response is not a view.": "The response is not a view.", + "The team name does not match.": "The team name does not match.", + "This action cannot be undone. This will permanently delete the team \":name\".": "This action cannot be undone. This will permanently delete the team \":name\".", + "This action is unauthorized.": "This action is unauthorized.", + "This invitation has already been accepted.": "This invitation has already been accepted.", + "This invitation has expired.": "This invitation has expired.", + "This invitation was sent to a different email address.": "This invitation was sent to a different email address.", + "This is a secure area of the application. Please confirm your password before continuing.": "This is a secure area of the application. Please confirm your password before continuing.", + "This password reset link will expire in :count minutes.": "This password reset link will expire in :count minutes.", + "This team name is reserved and cannot be used.": "This team name is reserved and cannot be used.", + "This user is already a member of the team.": "This user is already a member of the team.", + "to": "to", + "To finish enabling two-factor authentication, scan the QR code or enter the setup key in your authenticator app.": "To finish enabling two-factor authentication, scan the QR code or enter the setup key in your authenticator app.", + "Toggle navigation": "Toggle navigation", + "Too Early": "Too Early", + "Too Many Requests": "Too Many Requests", + "Translate": "Translate", + "Translate It": "Translate It", + "Two-factor authentication": "Two-factor authentication", + "Two-factor authentication enabled": "Two-factor authentication enabled", + "Two-factor authentication is now enabled. Scan the QR code or enter the setup key in your authenticator app.": "Two-factor authentication is now enabled. Scan the QR code or enter the setup key in your authenticator app.", + "Type \":name\" to confirm": "Type \":name\" to confirm", + "Unable to register passkey. Please try again.": "Unable to register passkey. Please try again.", + "Unable to register this passkey.": "Unable to register this passkey.", + "Unable to sign in with this account.": "Unable to sign in with this account.", + "Unable to verify passkey. Please try again.": "Unable to verify passkey. Please try again.", + "Unable to write the encrypted environment file.": "Unable to write the encrypted environment file.", + "Unauthorized": "Unauthorized", + "Unavailable For Legal Reasons": "Unavailable For Legal Reasons", + "Unknown Error": "Unknown Error", + "Unpack": "Unpack", + "Unprocessable Entity": "Unprocessable Entity", + "Unsubscribe": "Unsubscribe", + "Unsupported Media Type": "Unsupported Media Type", + "Up": "Up", + "Update": "Update", + "Update :name": "Update :name", + "Update password": "Update password", + "Update the appearance settings for your account": "Update the appearance settings for your account", + "Update your name and email address": "Update your name and email address", + "Upgrade Required": "Upgrade Required", + "URI Too Long": "URI Too Long", + "Use Proxy": "Use Proxy", + "User": "User", + "Variant Also Negotiates": "Variant Also Negotiates", + "Verify authentication code": "Verify authentication code", + "Verify Email Address": "Verify Email Address", + "Verify your email address": "Verify your email address", + "View": "View", + "View :name": "View :name", + "View recovery codes": "View recovery codes", + "View team": "View team", + "Warning": "Warning", + "Web Server is Down": "Web Server is Down", + "Welcome": "Welcome", + "When you enable two-factor authentication, you will be prompted for a secure pin during login. This pin can be retrieved from a TOTP-supported application on your phone.": "When you enable two-factor authentication, you will be prompted for a secure pin during login. This pin can be retrieved from a TOTP-supported application on your phone.", + "Whoops!": "Whoops!", + "Yes": "Yes", + "You are receiving this email because we received a password reset request for your account.": "You are receiving this email because we received a password reset request for your account.", + "You don't belong to any teams yet.": "You don't belong to any teams yet.", + "You left the team \":name\"": "You left the team \":name\"", + "You will be prompted for a secure, random pin during login, which you can retrieve from the TOTP-supported application on your phone.": "You will be prompted for a secure, random pin during login, which you can retrieve from the TOTP-supported application on your phone.", + "You've been invited to join :teamName": "You've been invited to join :teamName", + "Your email address is unverified.": "Your email address is unverified." +} \ No newline at end of file diff --git a/lang/en/actions.php b/lang/en/actions.php new file mode 100644 index 0000000..7ff2b68 --- /dev/null +++ b/lang/en/actions.php @@ -0,0 +1,119 @@ + 'Accept', + 'action' => 'Action', + 'actions' => 'Actions', + 'add' => 'Add', + 'admin' => 'Admin', + 'agree' => 'Agree', + 'archive' => 'Archive', + 'assign' => 'Assign', + 'associate' => 'Associate', + 'attach' => 'Attach', + 'browse' => 'Browse', + 'cancel' => 'Cancel', + 'choose' => 'Choose', + 'choose_file' => 'Choose File', + 'choose_image' => 'Choose Image', + 'click_to_copy' => 'Click to copy', + 'close' => 'Close', + 'collapse' => 'Collapse', + 'collapse_all' => 'Collapse All', + 'comment' => 'Comment', + 'confirm' => 'Confirm', + 'connect' => 'Connect', + 'create' => 'Create', + 'delete' => 'Delete', + 'detach' => 'Detach', + 'details' => 'Details', + 'disable' => 'Disable', + 'discard' => 'Discard', + 'done' => 'Done', + 'down' => 'Down', + 'duplicate' => 'Duplicate', + 'edit' => 'Edit', + 'enable' => 'Enable', + 'expand' => 'Expand', + 'expand_all' => 'Expand All', + 'explanation' => 'Explanation', + 'export' => 'Export', + 'file' => 'File', + 'files' => 'Files', + 'go_home' => 'Go Home', + 'hide' => 'Hide', + 'home' => 'Home', + 'image' => 'Image', + 'impersonate' => 'Impersonate', + 'impersonation' => 'Impersonation', + 'import' => 'Import', + 'introduction' => 'Introduction', + 'like' => 'Like', + 'load' => 'Load', + 'localize' => 'Localize', + 'log_in' => 'Log In', + 'log_out' => 'Log Out', + 'named' => [ + 'add' => 'Add :name', + 'choose' => 'Choose :name', + 'create' => 'Create :name', + 'delete' => 'Delete :name', + 'duplicate' => 'Duplicate :name', + 'edit' => 'Edit :name', + 'export' => 'Export :name', + 'hide' => 'Hide :name', + 'import' => 'Import :name', + 'new' => 'New :name', + 'restore' => 'Restore :name', + 'save' => 'Save :name', + 'search' => 'Search :name', + 'show' => 'Show :name', + 'update' => 'Update :name', + 'view' => 'View :name', + ], + 'new' => 'New', + 'no' => 'No', + 'open' => 'Open', + 'open_website' => 'Open on the website', + 'preview' => 'Preview', + 'price' => 'Price', + 'record' => 'Record', + 'restore' => 'Restore', + 'save' => 'Save', + 'save_and_close' => 'Save & Close', + 'save_and_return' => 'Save & Return', + 'search' => 'Search', + 'select' => 'Select', + 'select_all' => 'Select All', + 'send' => 'Send', + 'settings' => 'Settings', + 'show' => 'Show', + 'show_all' => 'Show All', + 'sign_in' => 'Sign In', + 'solve' => 'Solve', + 'start' => 'Start', + 'stop' => 'Stop', + 'submit' => 'Submit', + 'subscribe' => 'Subscribe', + 'switch' => 'Switch', + 'switch_to_role' => 'Switch To Role', + 'tag' => 'Tag', + 'tags' => 'Tags', + 'target_link' => [ + 'blank' => 'Open in a new window', + 'parent' => 'Open in a parent frame', + 'self' => 'Open in a current window', + 'top' => 'Open in the topmost frame', + ], + 'translate' => 'Translate', + 'translate_it' => 'Translate It', + 'unpack' => 'Unpack', + 'unsubscribe' => 'Unsubscribe', + 'up' => 'Up', + 'update' => 'Update', + 'user' => 'User', + 'view' => 'View', + 'yes' => 'Yes', +]; diff --git a/lang/en/auth.php b/lang/en/auth.php new file mode 100644 index 0000000..ad725d8 --- /dev/null +++ b/lang/en/auth.php @@ -0,0 +1,9 @@ + 'These credentials do not match our records.', + 'password' => 'The provided password is incorrect.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', +]; diff --git a/lang/en/http-statuses.php b/lang/en/http-statuses.php new file mode 100644 index 0000000..3346f78 --- /dev/null +++ b/lang/en/http-statuses.php @@ -0,0 +1,84 @@ + 'Unknown Error', + '100' => 'Continue', + '101' => 'Switching Protocols', + '102' => 'Processing', + '200' => 'OK', + '201' => 'Created', + '202' => 'Accepted', + '203' => 'Non-Authoritative Information', + '204' => 'No Content', + '205' => 'Reset Content', + '206' => 'Partial Content', + '207' => 'Multi-Status', + '208' => 'Already Reported', + '226' => 'IM Used', + '300' => 'Multiple Choices', + '301' => 'Moved Permanently', + '302' => 'Found', + '303' => 'See Other', + '304' => 'Not Modified', + '305' => 'Use Proxy', + '307' => 'Temporary Redirect', + '308' => 'Permanent Redirect', + '400' => 'Bad Request', + '401' => 'Unauthorized', + '402' => 'Payment Required', + '403' => 'Forbidden', + '404' => 'Not Found', + '405' => 'Method Not Allowed', + '406' => 'Not Acceptable', + '407' => 'Proxy Authentication Required', + '408' => 'Request Timeout', + '409' => 'Conflict', + '410' => 'Gone', + '411' => 'Length Required', + '412' => 'Precondition Failed', + '413' => 'Payload Too Large', + '414' => 'URI Too Long', + '415' => 'Unsupported Media Type', + '416' => 'Range Not Satisfiable', + '417' => 'Expectation Failed', + '418' => 'I\'m a teapot', + '419' => 'Session Has Expired', + '421' => 'Misdirected Request', + '422' => 'Unprocessable Entity', + '423' => 'Locked', + '424' => 'Failed Dependency', + '425' => 'Too Early', + '426' => 'Upgrade Required', + '428' => 'Precondition Required', + '429' => 'Too Many Requests', + '431' => 'Request Header Fields Too Large', + '444' => 'Connection Closed Without Response', + '449' => 'Retry With', + '451' => 'Unavailable For Legal Reasons', + '499' => 'Client Closed Request', + '500' => 'Internal Server Error', + '501' => 'Not Implemented', + '502' => 'Bad Gateway', + '503' => 'Maintenance Mode', + '504' => 'Gateway Timeout', + '505' => 'HTTP Version Not Supported', + '506' => 'Variant Also Negotiates', + '507' => 'Insufficient Storage', + '508' => 'Loop Detected', + '509' => 'Bandwidth Limit Exceeded', + '510' => 'Not Extended', + '511' => 'Network Authentication Required', + '520' => 'Unknown Error', + '521' => 'Web Server is Down', + '522' => 'Connection Timed Out', + '523' => 'Origin Is Unreachable', + '524' => 'A Timeout Occurred', + '525' => 'SSL Handshake Failed', + '526' => 'Invalid SSL Certificate', + '527' => 'Railgun Error', + '598' => 'Network Read Timeout Error', + '599' => 'Network Connect Timeout Error', + 'unknownError' => 'Unknown Error', +]; diff --git a/lang/en/pagination.php b/lang/en/pagination.php new file mode 100644 index 0000000..f4cedde --- /dev/null +++ b/lang/en/pagination.php @@ -0,0 +1,8 @@ + 'Next »', + 'previous' => '« Previous', +]; diff --git a/lang/en/passwords.php b/lang/en/passwords.php new file mode 100644 index 0000000..f3b65ba --- /dev/null +++ b/lang/en/passwords.php @@ -0,0 +1,11 @@ + 'Your password has been reset.', + 'sent' => 'We have emailed your password reset link.', + 'throttled' => 'Please wait before retrying.', + 'token' => 'This password reset token is invalid.', + 'user' => 'We can\'t find a user with that email address.', +]; diff --git a/lang/en/validation.php b/lang/en/validation.php new file mode 100644 index 0000000..bafa853 --- /dev/null +++ b/lang/en/validation.php @@ -0,0 +1,290 @@ + 'The :attribute field must be accepted.', + 'accepted_if' => 'The :attribute field must be accepted when :other is :value.', + 'active_url' => 'The :attribute field must be a valid URL.', + 'after' => 'The :attribute field must be a date after :date.', + 'after_or_equal' => 'The :attribute field must be a date after or equal to :date.', + 'alpha' => 'The :attribute field must only contain letters.', + 'alpha_dash' => 'The :attribute field must only contain letters, numbers, dashes, and underscores.', + 'alpha_num' => 'The :attribute field must only contain letters and numbers.', + 'any_of' => 'The :attribute field is invalid.', + 'array' => 'The :attribute field must be an array.', + 'array_keys' => 'The :attribute field must only contain the following keys: :values.', + 'ascii' => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.', + 'base64' => 'The :attribute field must be a valid Base64 string.', + 'before' => 'The :attribute field must be a date before :date.', + 'before_or_equal' => 'The :attribute field must be a date before or equal to :date.', + 'between' => [ + 'array' => 'The :attribute field must have between :min and :max items.', + 'file' => 'The :attribute field must be between :min and :max kilobytes.', + 'numeric' => 'The :attribute field must be between :min and :max.', + 'string' => 'The :attribute field must be between :min and :max characters.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'can' => 'The :attribute field contains an unauthorized value.', + 'confirmed' => 'The :attribute field confirmation does not match.', + 'contains' => 'The :attribute field is missing a required value.', + 'current_password' => 'The password is incorrect.', + 'date' => 'The :attribute field must be a valid date.', + 'date_equals' => 'The :attribute field must be a date equal to :date.', + 'date_format' => 'The :attribute field must match the format :format.', + 'decimal' => 'The :attribute field must have :decimal decimal places.', + 'declined' => 'The :attribute field must be declined.', + 'declined_if' => 'The :attribute field must be declined when :other is :value.', + 'different' => 'The :attribute field and :other must be different.', + 'digits' => 'The :attribute field must be :digits digits.', + 'digits_between' => 'The :attribute field must be between :min and :max digits.', + 'dimensions' => 'The :attribute field has invalid image dimensions.', + 'distinct' => 'The :attribute field has a duplicate value.', + 'doesnt_contain' => 'The :attribute field must not contain any of the following: :values.', + 'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.', + 'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.', + 'email' => 'The :attribute field must be a valid email address.', + 'encoding' => 'The :attribute field must be encoded in :encoding.', + 'ends_with' => 'The :attribute field must end with one of the following: :values.', + 'enum' => 'The selected :attribute is invalid.', + 'exists' => 'The selected :attribute is invalid.', + 'extensions' => 'The :attribute field must have one of the following extensions: :values.', + 'file' => 'The :attribute field must be a file.', + 'filled' => 'The :attribute field must have a value.', + 'gt' => [ + 'array' => 'The :attribute field must have more than :value items.', + 'file' => 'The :attribute field must be greater than :value kilobytes.', + 'numeric' => 'The :attribute field must be greater than :value.', + 'string' => 'The :attribute field must be greater than :value characters.', + ], + 'gte' => [ + 'array' => 'The :attribute field must have :value items or more.', + 'file' => 'The :attribute field must be greater than or equal to :value kilobytes.', + 'numeric' => 'The :attribute field must be greater than or equal to :value.', + 'string' => 'The :attribute field must be greater than or equal to :value characters.', + ], + 'hex_color' => 'The :attribute field must be a valid hexadecimal color.', + 'image' => 'The :attribute field must be an image.', + 'in' => 'The selected :attribute is invalid.', + 'in_array' => 'The :attribute field must exist in :other.', + 'in_array_keys' => 'The :attribute field must contain at least one of the following keys: :values.', + 'integer' => 'The :attribute field must be an integer.', + 'ip' => 'The :attribute field must be a valid IP address.', + 'ipv4' => 'The :attribute field must be a valid IPv4 address.', + 'ipv6' => 'The :attribute field must be a valid IPv6 address.', + 'json' => 'The :attribute field must be a valid JSON string.', + 'list' => 'The :attribute field must be a list.', + 'lowercase' => 'The :attribute field must be lowercase.', + 'lt' => [ + 'array' => 'The :attribute field must have less than :value items.', + 'file' => 'The :attribute field must be less than :value kilobytes.', + 'numeric' => 'The :attribute field must be less than :value.', + 'string' => 'The :attribute field must be less than :value characters.', + ], + 'lte' => [ + 'array' => 'The :attribute field must not have more than :value items.', + 'file' => 'The :attribute field must be less than or equal to :value kilobytes.', + 'numeric' => 'The :attribute field must be less than or equal to :value.', + 'string' => 'The :attribute field must be less than or equal to :value characters.', + ], + 'mac_address' => 'The :attribute field must be a valid MAC address.', + 'max' => [ + 'array' => 'The :attribute field must not have more than :max items.', + 'file' => 'The :attribute field must not be greater than :max kilobytes.', + 'numeric' => 'The :attribute field must not be greater than :max.', + 'string' => 'The :attribute field must not be greater than :max characters.', + ], + 'max_digits' => 'The :attribute field must not have more than :max digits.', + 'mimes' => 'The :attribute field must be a file of type: :values.', + 'mimetypes' => 'The :attribute field must be a file of type: :values.', + 'min' => [ + 'array' => 'The :attribute field must have at least :min items.', + 'file' => 'The :attribute field must be at least :min kilobytes.', + 'numeric' => 'The :attribute field must be at least :min.', + 'string' => 'The :attribute field must be at least :min characters.', + ], + 'min_digits' => 'The :attribute field must have at least :min digits.', + 'missing' => 'The :attribute field must be missing.', + 'missing_if' => 'The :attribute field must be missing when :other is :value.', + 'missing_unless' => 'The :attribute field must be missing unless :other is :value.', + 'missing_with' => 'The :attribute field must be missing when :values is present.', + 'missing_with_all' => 'The :attribute field must be missing when :values are present.', + 'multiple_of' => 'The :attribute field must be a multiple of :value.', + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute field format is invalid.', + 'numeric' => 'The :attribute field must be a number.', + 'password' => [ + 'letters' => 'The :attribute field must contain at least one letter.', + 'mixed' => 'The :attribute field must contain at least one uppercase and one lowercase letter.', + 'numbers' => 'The :attribute field must contain at least one number.', + 'symbols' => 'The :attribute field must contain at least one symbol.', + 'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.', + ], + 'present' => 'The :attribute field must be present.', + 'present_if' => 'The :attribute field must be present when :other is :value.', + 'present_unless' => 'The :attribute field must be present unless :other is :value.', + 'present_with' => 'The :attribute field must be present when :values is present.', + 'present_with_all' => 'The :attribute field must be present when :values are present.', + 'prohibited' => 'The :attribute field is prohibited.', + 'prohibited_if' => 'The :attribute field is prohibited when :other is :value.', + 'prohibited_if_accepted' => 'The :attribute field is prohibited when :other is accepted.', + 'prohibited_if_declined' => 'The :attribute field is prohibited when :other is declined.', + 'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.', + 'prohibits' => 'The :attribute field prohibits :other from being present.', + 'regex' => 'The :attribute field format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_array_keys' => 'The :attribute field must contain entries for: :values.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_if_accepted' => 'The :attribute field is required when :other is accepted.', + 'required_if_declined' => 'The :attribute field is required when :other is declined.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values are present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute field must match :other.', + 'size' => [ + 'array' => 'The :attribute field must contain :size items.', + 'file' => 'The :attribute field must be :size kilobytes.', + 'numeric' => 'The :attribute field must be :size.', + 'string' => 'The :attribute field must be :size characters.', + ], + 'starts_with' => 'The :attribute field must start with one of the following: :values.', + 'string' => 'The :attribute field must be a string.', + 'timezone' => 'The :attribute field must be a valid timezone.', + 'ulid' => 'The :attribute field must be a valid ULID.', + 'unique' => 'The :attribute has already been taken.', + 'uploaded' => 'The :attribute failed to upload.', + 'uppercase' => 'The :attribute field must be uppercase.', + 'url' => 'The :attribute field must be a valid URL.', + 'uuid' => 'The :attribute field must be a valid UUID.', + 'attributes' => [ + 'address' => 'address', + 'affiliate_url' => 'affiliate URL', + 'age' => 'age', + 'amount' => 'amount', + 'announcement' => 'announcement', + 'area' => 'area', + 'audience_prize' => 'audience prize', + 'audience_winner' => 'audience winner', + 'available' => 'available', + 'birthday' => 'birthday', + 'body' => 'body', + 'city' => 'city', + 'color' => 'color', + 'company' => 'company', + 'compilation' => 'compilation', + 'concept' => 'concept', + 'conditions' => 'conditions', + 'content' => 'content', + 'contest' => 'contest', + 'country' => 'country', + 'cover' => 'cover', + 'created_at' => 'created at', + 'creator' => 'creator', + 'currency' => 'currency', + 'current_password' => 'current password', + 'customer' => 'customer', + 'date' => 'date', + 'date_of_birth' => 'date of birth', + 'dates' => 'dates', + 'day' => 'day', + 'deleted_at' => 'deleted at', + 'description' => 'description', + 'display_type' => 'display type', + 'district' => 'district', + 'duration' => 'duration', + 'email' => 'email', + 'excerpt' => 'excerpt', + 'filter' => 'filter', + 'finished_at' => 'finished at', + 'first_name' => 'first name', + 'gender' => 'gender', + 'grand_prize' => 'grand prize', + 'group' => 'group', + 'hour' => 'hour', + 'image' => 'image', + 'image_desktop' => 'desktop image', + 'image_main' => 'main image', + 'image_mobile' => 'mobile image', + 'images' => 'images', + 'is_audience_winner' => 'is audience winner', + 'is_hidden' => 'is hidden', + 'is_subscribed' => 'is subscribed', + 'is_visible' => 'is visible', + 'is_winner' => 'is winner', + 'items' => 'items', + 'key' => 'key', + 'last_name' => 'last name', + 'lesson' => 'lesson', + 'line_address_1' => 'line address 1', + 'line_address_2' => 'line address 2', + 'login' => 'login', + 'message' => 'message', + 'middle_name' => 'middle name', + 'minute' => 'minute', + 'mobile' => 'mobile', + 'month' => 'month', + 'name' => 'name', + 'national_code' => 'national code', + 'number' => 'number', + 'password' => 'password', + 'password_confirmation' => 'password confirmation', + 'phone' => 'phone', + 'photo' => 'photo', + 'portfolio' => 'portfolio', + 'postal_code' => 'postal code', + 'preview' => 'preview', + 'price' => 'price', + 'product_id' => 'product ID', + 'product_uid' => 'product UID', + 'product_uuid' => 'product UUID', + 'promo_code' => 'promo code', + 'province' => 'province', + 'quantity' => 'quantity', + 'reason' => 'reason', + 'recaptcha_response_field' => 'recaptcha response field', + 'referee' => 'referee', + 'referees' => 'referees', + 'region' => 'region', + 'reject_reason' => 'reject reason', + 'remember' => 'remember', + 'restored_at' => 'restored at', + 'result_text_under_image' => 'result text under image', + 'role' => 'role', + 'rule' => 'rule', + 'rules' => 'rules', + 'second' => 'second', + 'sex' => 'sex', + 'shipment' => 'shipment', + 'short_text' => 'short text', + 'size' => 'size', + 'skills' => 'skills', + 'slug' => 'slug', + 'specialization' => 'specialization', + 'started_at' => 'started at', + 'state' => 'state', + 'status' => 'status', + 'street' => 'street', + 'student' => 'student', + 'subject' => 'subject', + 'tag' => 'tag', + 'tags' => 'tags', + 'teacher' => 'teacher', + 'terms' => 'terms', + 'test_description' => 'test description', + 'test_locale' => 'test locale', + 'test_name' => 'test name', + 'text' => 'text', + 'time' => 'time', + 'title' => 'title', + 'type' => 'type', + 'updated_at' => 'updated at', + 'user' => 'user', + 'username' => 'username', + 'value' => 'value', + 'winner' => 'winner', + 'work' => 'work', + 'year' => 'year', + ], +]; diff --git a/lang/project/de.json b/lang/project/de.json new file mode 100644 index 0000000..ff66e08 --- /dev/null +++ b/lang/project/de.json @@ -0,0 +1,18 @@ +{ + "Authenticating...": "Anmeldung läuft …", + "Click here to re-send the verification email.": "Klicken Sie hier, um die Bestätigungs-E-Mail erneut zu senden.", + "Confirming...": "Bestätigung läuft …", + "Email password reset link": "Link zum Zurücksetzen senden", + "Email verification": "E-Mail-Bestätigung", + "Enable two-factor authentication": "Zwei-Faktor-Authentifizierung aktivieren", + "Language": "Sprache", + "log in": "Anmeldung", + "login using a recovery code": "sich mit einem Wiederherstellungscode anmelden", + "login using an authentication code": "sich mit einem Authentifizierungscode anmelden", + "Or, return to": "Oder zurück zur", + "Passkey name": "Name des Passkeys", + "Registering...": "Registrierung läuft …", + "Sign up": "Registrieren", + "Theme": "Farbschema", + "Your email address is unverified.": "Ihre E-Mail-Adresse ist noch nicht bestätigt." +} diff --git a/pint.json b/pint.json index 93061b6..1d4157e 100644 --- a/pint.json +++ b/pint.json @@ -1,3 +1,6 @@ { - "preset": "laravel" + "preset": "laravel", + "exclude": [ + "lang" + ] } diff --git a/resources/views/livewire/settings/appearance.blade.php b/resources/views/livewire/settings/appearance.blade.php index 20a5d39..4cbfc35 100644 --- a/resources/views/livewire/settings/appearance.blade.php +++ b/resources/views/livewire/settings/appearance.blade.php @@ -4,7 +4,21 @@ {{ __('Appearance settings') }} - {{-- Hell / Dunkel / System; nutzt den Theme-Zustand aus tallstackui_darkTheme() am -Element. --}} - +
+
+ {{ __('Theme') }} + {{-- Hell / Dunkel / System; nutzt den Theme-Zustand aus tallstackui_darkTheme() am -Element. --}} + +
+ +
+ {{ __('Language') }} + {{-- Sprachnamen bewusst in der jeweiligen Sprache, nicht übersetzt. --}} + +
+
diff --git a/resources/views/partials/head.blade.php b/resources/views/partials/head.blade.php index 88cfe0e..6b7c9cc 100644 --- a/resources/views/partials/head.blade.php +++ b/resources/views/partials/head.blade.php @@ -2,7 +2,7 @@ - {{ filled($title ?? null) ? $title.' - '.config('app.name', 'Laravel') : config('app.name', 'Laravel') }} + {{ filled($title ?? null) ? __($title).' - '.config('app.name', 'Laravel') : config('app.name', 'Laravel') }} diff --git a/tests/Feature/LocaleTest.php b/tests/Feature/LocaleTest.php new file mode 100644 index 0000000..2b157c1 --- /dev/null +++ b/tests/Feature/LocaleTest.php @@ -0,0 +1,82 @@ +get(route('login')) + ->assertOk() + ->assertSee('assertSee('Melden Sie sich bei Ihrem Konto an'); + } + + public function test_the_language_of_the_user_is_used(): void + { + $user = User::factory()->create(['locale' => 'en']); + + $this->actingAs($user) + ->get(route('profile.edit')) + ->assertOk() + ->assertSee('assertSee('Update your name and email address'); + } + + public function test_an_unknown_stored_language_falls_back_to_the_default(): void + { + $user = User::factory()->create(['locale' => 'xx']); + + $this->actingAs($user) + ->get(route('profile.edit')) + ->assertOk() + ->assertSee('actingAs(User::factory()->create()) + ->get(route('appearance.edit')) + ->assertOk() + ->assertSee('data-test="locale-select"', false) + ->assertSee('wire:model.live="locale"', false); + + foreach (config('app.available_locales') as $code => $label) { + $response->assertSee('value="'.$code.'"', false)->assertSee($label); + } + } + + public function test_users_can_change_their_language(): void + { + $user = User::factory()->create(); + + Livewire::actingAs($user) + ->test(Appearance::class) + ->assertSet('locale', 'de') + ->set('locale', 'en') + ->assertHasNoErrors() + ->assertRedirect(route('appearance.edit')); + + $this->assertSame('en', $user->fresh()->locale); + } + + public function test_only_offered_languages_can_be_chosen(): void + { + $user = User::factory()->create(); + + Livewire::actingAs($user) + ->test(Appearance::class) + ->set('locale', 'xx') + ->assertHasErrors('locale'); + + $this->assertNull($user->fresh()->locale); + } +} diff --git a/tests/Feature/Settings/SecurityTest.php b/tests/Feature/Settings/SecurityTest.php index 9bbb7ea..83f6509 100644 --- a/tests/Feature/Settings/SecurityTest.php +++ b/tests/Feature/Settings/SecurityTest.php @@ -39,10 +39,10 @@ class SecurityTest extends TestCase $response->assertOk(); - $response->assertSee('Passkeys'); - $response->assertSee('No passkeys yet'); - $response->assertSee('Two-factor authentication'); - $response->assertSee('Enable 2FA'); + $response->assertSee(__('Passkeys')); + $response->assertSee(__('No passkeys yet')); + $response->assertSee(__('Two-factor authentication')); + $response->assertSee(__('Enable 2FA')); } public function test_security_settings_page_requires_password_confirmation_when_enabled(): void @@ -65,10 +65,10 @@ class SecurityTest extends TestCase ->withSession(['auth.password_confirmed_at' => time()]) ->get(route('security.edit')) ->assertOk() - ->assertSee('Update password') - ->assertDontSee('Manage your passkeys for passwordless sign-in') - ->assertDontSee('Add a passkey to sign in without a password') - ->assertDontSee('Two-factor authentication'); + ->assertSee(__('Update password')) + ->assertDontSee(__('Manage your passkeys for passwordless sign-in')) + ->assertDontSee(__('Add a passkey to sign in without a password')) + ->assertDontSee(__('Two-factor authentication')); } public function test_two_factor_authentication_disabled_when_confirmation_abandoned_between_requests(): void diff --git a/tests/Unit/TranslationCoverageTest.php b/tests/Unit/TranslationCoverageTest.php new file mode 100644 index 0000000..d6665b1 --- /dev/null +++ b/tests/Unit/TranslationCoverageTest.php @@ -0,0 +1,76 @@ +translationKeysUsedInCode(); + $this->assertNotEmpty($keys); + + foreach (array_keys(config('app.available_locales')) as $locale) { + if ($locale === 'en') { + continue; + } + + $translations = json_decode(File::get(lang_path("{$locale}.json")), true, flags: JSON_THROW_ON_ERROR); + $missing = array_values(array_diff($keys, array_keys($translations))); + + $this->assertSame([], $missing, "Fehlende Übersetzungen in lang/{$locale}.json"); + } + } + + public function test_project_translations_are_applied(): void + { + foreach (File::glob(lang_path('project/*.json')) as $projectFile) { + $locale = pathinfo($projectFile, PATHINFO_FILENAME); + + $project = json_decode(File::get($projectFile), true, flags: JSON_THROW_ON_ERROR); + $translations = json_decode(File::get(lang_path("{$locale}.json")), true, flags: JSON_THROW_ON_ERROR); + + $notApplied = array_keys(array_diff_assoc($project, array_intersect_key($translations, $project))); + + $this->assertSame([], $notApplied, "lang/project/{$locale}.json nicht übernommen – `sail artisan lang:apply-project` ausführen"); + } + } + + /** + * @return list + */ + private function translationKeysUsedInCode(): array + { + $patterns = [ + '/__\(\s*\'((?:[^\'\\\\]|\\\\.)*)\'/', + '/__\(\s*"((?:[^"\\\\]|\\\\.)*)"/', + '/#\[Title\(\s*\'((?:[^\'\\\\]|\\\\.)*)\'/', + ]; + + $keys = []; + + foreach ([resource_path('views'), app_path()] as $directory) { + foreach (File::allFiles($directory) as $file) { + if ($file->getExtension() !== 'php') { + continue; + } + + foreach ($patterns as $pattern) { + preg_match_all($pattern, $file->getContents(), $matches); + + foreach ($matches[1] as $key) { + $keys[] = stripslashes($key); + } + } + } + } + + return array_values(array_unique($keys)); + } +}