Compare commits
29 Commits
25613eea05
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ea8b3f6b6 | |||
| d52968e697 | |||
| bed7137e43 | |||
| 028e0b11c7 | |||
| 5bf43abab9 | |||
| f96ad4d14f | |||
| a4aff43091 | |||
| 1e20982e00 | |||
| ec7aaf44a9 | |||
| 00a5951654 | |||
| 8dba510a6c | |||
| e66ece71e3 | |||
| b96012f598 | |||
| e50ed261e1 | |||
| 79a09430aa | |||
| 33fef93ce0 | |||
| e14cc5dd43 | |||
| c9fe1f6ed0 | |||
| 01c92aeb7c | |||
| 211867e2eb | |||
| f57d235651 | |||
| b419991940 | |||
| 61d42d79f1 | |||
| 5b83936c4b | |||
| ef238891f5 | |||
| a9b4b93d8c | |||
| 9625268e67 | |||
| ac56889a87 | |||
| 893d3c7a69 |
97
README.md
97
README.md
@@ -12,6 +12,20 @@ Markdown対応のナレッジベースアプリケーションです。Wiki風
|
||||
- 👥 **ユーザー管理** - 管理者によるユーザーのCRUD操作
|
||||
- 🔒 **認証** - Laravel Breezeによるログイン/登録機能
|
||||
- 🎨 **シンタックスハイライト** - コードブロックの自動ハイライト
|
||||
- 🌐 **多言語対応** - 8言語サポート(ユーザーごとに言語設定可能)
|
||||
|
||||
## 対応言語
|
||||
|
||||
| 言語 | コード |
|
||||
|------|--------|
|
||||
| English | en |
|
||||
| 日本語 | ja |
|
||||
| Deutsch | de |
|
||||
| Français | fr |
|
||||
| Español | es |
|
||||
| 简体中文 | zh-CN |
|
||||
| 繁體中文 | zh-TW |
|
||||
| 한국어 | ko |
|
||||
|
||||
## 技術スタック
|
||||
|
||||
@@ -49,6 +63,7 @@ Markdown対応のナレッジベースアプリケーションです。Wiki風
|
||||
│ │ ├── Livewire/ # Livewireコンポーネント
|
||||
│ │ ├── Models/ # Eloquentモデル
|
||||
│ │ └── Services/ # ビジネスロジック
|
||||
│ ├── lang/ # 言語ファイル(i18n)
|
||||
│ ├── resources/views/
|
||||
│ │ ├── layouts/ # レイアウト
|
||||
│ │ ├── livewire/ # Livewireビュー
|
||||
@@ -165,6 +180,62 @@ DocumentSeederを実行すると以下のドキュメントが作成されます
|
||||
|
||||
※ 既存のドキュメントがある場合、DocumentSeederはスキップされます。
|
||||
|
||||
## 本番環境へのデプロイ
|
||||
|
||||
### ⚠️ 重要: サブドメインを使用してください
|
||||
|
||||
このアプリケーションは **サブドメイン** でのデプロイを推奨します。
|
||||
|
||||
```
|
||||
✅ 推奨: kb.example.com
|
||||
❌ 非推奨: example.com/kb (サブディレクトリ)
|
||||
```
|
||||
|
||||
**理由**: Livewire 3はサブディレクトリデプロイに完全対応していません。`/livewire/update` エンドポイントがサブディレクトリを考慮しないため、AJAX通信が失敗します。
|
||||
|
||||
### デプロイ手順
|
||||
|
||||
1. **サブドメインのDNS設定**
|
||||
```
|
||||
kb.example.com → サーバーIPアドレス
|
||||
```
|
||||
|
||||
2. **Webサーバー設定**(nginx例)
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name kb.example.com;
|
||||
root /var/www/knowledge-base/src/public;
|
||||
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
|
||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **環境変数の設定**
|
||||
```env
|
||||
APP_URL=https://kb.example.com
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
```
|
||||
|
||||
4. **本番用の最適化**
|
||||
```bash
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
php artisan optimize
|
||||
```
|
||||
|
||||
## よく使うコマンド
|
||||
|
||||
```bash
|
||||
@@ -181,6 +252,12 @@ docker exec kb_php npm [command]
|
||||
|
||||
# コンテナに入る
|
||||
docker exec -it kb_php bash
|
||||
|
||||
# キャッシュクリア
|
||||
docker exec kb_php php artisan config:clear
|
||||
docker exec kb_php php artisan route:clear
|
||||
docker exec kb_php php artisan view:clear
|
||||
docker exec kb_php php artisan cache:clear
|
||||
```
|
||||
|
||||
## 管理者機能
|
||||
@@ -219,6 +296,15 @@ docker exec -it kb_php bash
|
||||
[[Laravel/Livewire/Components]] も確認してください。
|
||||
```
|
||||
|
||||
## 言語設定
|
||||
|
||||
ユーザーは「プロフィール」ページから使用言語を変更できます。
|
||||
|
||||
1. 右上のユーザー名をクリック
|
||||
2. 「プロフィール」を選択
|
||||
3. 「言語設定」セクションで言語を選択
|
||||
4. 「保存」をクリック
|
||||
|
||||
## トラブルシューティング
|
||||
|
||||
### パーミッションエラー
|
||||
@@ -243,6 +329,17 @@ docker compose up -d
|
||||
|
||||
`src/.env` の `DB_HOST` が `kb_mysql` になっているか確認してください。
|
||||
|
||||
### Livewireのエラー(本番環境)
|
||||
|
||||
「404 /livewire/update」エラーが出る場合は、サブディレクトリではなくサブドメインでデプロイしてください。
|
||||
|
||||
```bash
|
||||
# キャッシュをクリア
|
||||
php artisan config:clear
|
||||
php artisan route:clear
|
||||
php artisan cache:clear
|
||||
```
|
||||
|
||||
## ライセンス
|
||||
|
||||
MIT License
|
||||
|
||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "knowledge-base",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ APP_URL=http://localhost
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
APP_TIMEZONE=Asia/Tokyo
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
355
src/README.md
355
src/README.md
@@ -1,59 +1,332 @@
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
# Knowledge Base System
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
An Obsidian-like knowledge base system built with Laravel 12, Livewire v3, and Alpine.js. Create, organize, and link your documents with wiki-style references and a powerful search interface.
|
||||
|
||||
## About Laravel
|
||||
## Features
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
### Core Functionality
|
||||
- **Markdown-based documents** with live preview using EasyMDE editor
|
||||
- **Wiki-style linking** with `[[Document Title]]` syntax
|
||||
- **Automatic backlinks** - see which documents reference the current page
|
||||
- **Folder organization** - use `/` in titles to auto-organize into folders (e.g., `Laravel/Livewire/Components`)
|
||||
- **Quick switcher** - Press `Ctrl+K` to instantly search and navigate
|
||||
- **Full-text search** - MySQL FULLTEXT index with ngram tokenizer for multilingual support
|
||||
- **ID-based routing** - Clean URLs with guaranteed uniqueness
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
### Multi-Language Support
|
||||
Interface available in **16 languages**:
|
||||
- English, 日本語 (Japanese)
|
||||
- 简体中文, 繁體中文 (Simplified/Traditional Chinese)
|
||||
- 한국어 (Korean)
|
||||
- हिन्दी (Hindi), Tiếng Việt (Vietnamese), Türkçe (Turkish)
|
||||
- Deutsch, Français, Español, Português (Brasil)
|
||||
- Русский, Українська, Italiano, Polski
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
Language preferences persist for both authenticated and guest users via cookies.
|
||||
|
||||
## Learning Laravel
|
||||
### Responsive Design
|
||||
- **Mobile-first** interface with hamburger menu
|
||||
- **Tablet and desktop** optimized layouts
|
||||
- **Touch-friendly** navigation
|
||||
- All features work seamlessly across devices
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application.
|
||||
### User Management
|
||||
- **Role-based access** - Admin and regular user roles
|
||||
- **User authentication** - Laravel Breeze integration
|
||||
- **Profile management** - Update name, email, password, and language preference
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
## Technology Stack
|
||||
|
||||
## Laravel Sponsors
|
||||
- **Backend**: Laravel 12.0 (PHP 8.2+)
|
||||
- **Frontend**: Livewire v3.7.0, Alpine.js, Tailwind CSS
|
||||
- **Database**: MySQL 8.0 with FULLTEXT indexing
|
||||
- **Markdown**: league/commonmark for rendering
|
||||
- **Editor**: EasyMDE (markdown editor)
|
||||
- **Docker**: Custom containerized environment
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
||||
## Prerequisites
|
||||
|
||||
### Premium Partners
|
||||
- Docker and Docker Compose
|
||||
- Node.js 18+ (for asset compilation)
|
||||
- Git
|
||||
|
||||
- **[Vehikl](https://vehikl.com)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel)**
|
||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
||||
- **[Redberry](https://redberry.international/laravel-development)**
|
||||
- **[Active Logic](https://activelogic.com)**
|
||||
## Installation
|
||||
|
||||
### 1. Clone the repository
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd knowledge-base
|
||||
```
|
||||
|
||||
### 2. Start Docker services
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This starts:
|
||||
- Nginx: `http://localhost:9700`
|
||||
- phpMyAdmin: `http://localhost:9701`
|
||||
- MySQL: `localhost:9702`
|
||||
- MailHog: `http://localhost:9725`
|
||||
|
||||
### 3. Install dependencies
|
||||
|
||||
```bash
|
||||
# Inside the src/ directory
|
||||
cd src
|
||||
|
||||
# Install PHP dependencies
|
||||
docker compose exec php composer install
|
||||
|
||||
# Install Node dependencies
|
||||
npm install
|
||||
```
|
||||
|
||||
### 4. Configure environment
|
||||
|
||||
```bash
|
||||
# Copy environment file
|
||||
cp .env.example .env
|
||||
|
||||
# Generate application key
|
||||
docker compose exec php php artisan key:generate
|
||||
```
|
||||
|
||||
### 5. Set up database
|
||||
|
||||
```bash
|
||||
# Run migrations
|
||||
docker compose exec php php artisan migrate
|
||||
|
||||
# Seed initial user (admin@example.com / password)
|
||||
docker compose exec php php artisan db:seed --class=UserSeeder
|
||||
|
||||
# Initialize sample documents (optional)
|
||||
docker compose exec php php artisan docs:init
|
||||
```
|
||||
|
||||
### 6. Build frontend assets
|
||||
|
||||
```bash
|
||||
# Development mode with hot reload
|
||||
npm run dev
|
||||
|
||||
# Or production build
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 7. Access the application
|
||||
|
||||
Open `http://localhost:9700` in your browser.
|
||||
|
||||
**Default credentials**:
|
||||
- Email: `admin@example.com`
|
||||
- Password: `password`
|
||||
|
||||
## Development
|
||||
|
||||
### Running the dev environment
|
||||
|
||||
```bash
|
||||
# Start all services (server, queue, logs, Vite)
|
||||
docker compose exec php composer dev
|
||||
```
|
||||
|
||||
### Running tests
|
||||
|
||||
```bash
|
||||
docker compose exec php php artisan test
|
||||
```
|
||||
|
||||
### Common commands
|
||||
|
||||
```bash
|
||||
# Access PHP container shell
|
||||
docker compose exec php bash
|
||||
|
||||
# Clear caches
|
||||
docker compose exec php php artisan config:clear
|
||||
docker compose exec php php artisan cache:clear
|
||||
docker compose exec php php artisan view:clear
|
||||
|
||||
# Publish Livewire assets (after updates)
|
||||
docker compose exec php php artisan livewire:publish --assets
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── app/
|
||||
│ ├── Http/
|
||||
│ │ ├── Controllers/
|
||||
│ │ │ └── LocaleController.php # Language switching
|
||||
│ │ └── Middleware/
|
||||
│ │ └── SetLocale.php # Multi-language support
|
||||
│ ├── Livewire/
|
||||
│ │ ├── DocumentEditor.php # Create/edit documents
|
||||
│ │ ├── DocumentViewer.php # Display documents
|
||||
│ │ ├── QuickSwitcher.php # Ctrl+K search modal
|
||||
│ │ └── SidebarTree.php # Folder tree navigation
|
||||
│ ├── Models/
|
||||
│ │ ├── Document.php # Document model
|
||||
│ │ ├── DocumentLink.php # Wiki-style links
|
||||
│ │ └── RecentDocument.php # Access history
|
||||
│ └── Services/
|
||||
│ └── DocumentService.php # Document business logic
|
||||
├── database/
|
||||
│ └── migrations/ # Database schema
|
||||
├── lang/ # Translation files (16 languages)
|
||||
├── resources/
|
||||
│ ├── css/
|
||||
│ │ └── app.css # Tailwind + custom styles
|
||||
│ ├── js/
|
||||
│ │ └── app.js # Alpine.js initialization
|
||||
│ └── views/
|
||||
│ ├── layouts/
|
||||
│ │ └── knowledge-base.blade.php # Main layout
|
||||
│ └── livewire/ # Livewire component views
|
||||
└── routes/
|
||||
└── web.php # Application routes
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Document Organization
|
||||
|
||||
Documents are organized using **virtual paths** derived from titles:
|
||||
|
||||
```php
|
||||
Title: "Laravel/Livewire/Components"
|
||||
→ Path: "Laravel/Livewire/Components.md"
|
||||
→ Slug: "components"
|
||||
→ Sidebar: Nested under Laravel → Livewire → Components
|
||||
```
|
||||
|
||||
No manual directory field needed - just use `/` in the title!
|
||||
|
||||
### Wiki-Style Links
|
||||
|
||||
Create links between documents using double brackets:
|
||||
|
||||
```markdown
|
||||
See [[Getting Started]] for more information.
|
||||
Links to [[Uncreated Pages]] appear in red.
|
||||
```
|
||||
|
||||
Links are automatically:
|
||||
- Extracted and stored in the `document_links` table
|
||||
- Rendered as clickable HTML anchors
|
||||
- Displayed as backlinks on target documents
|
||||
|
||||
### ID-Based Routing
|
||||
|
||||
URLs use document IDs instead of slugs:
|
||||
|
||||
```
|
||||
/documents/123 (instead of /documents/my-document-slug)
|
||||
```
|
||||
|
||||
Benefits:
|
||||
- Guaranteed uniqueness
|
||||
- Title changes don't break URLs
|
||||
- Simpler route model binding
|
||||
|
||||
### Folder State Persistence
|
||||
|
||||
Sidebar folder expanded/collapsed state is stored in `localStorage`:
|
||||
|
||||
```javascript
|
||||
// Managed by Alpine.js
|
||||
localStorage.getItem('kb_expanded_folders')
|
||||
// ["Laravel", "Laravel/Livewire", "Docker"]
|
||||
```
|
||||
|
||||
Survives page navigation and browser sessions.
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding new languages
|
||||
|
||||
1. Add to `SetLocale::SUPPORTED_LOCALES` in `app/Http/Middleware/SetLocale.php`
|
||||
2. Create translation file at `lang/{code}/messages.php`
|
||||
3. Copy structure from existing language file
|
||||
|
||||
### Changing default locale
|
||||
|
||||
Edit `config/app.php`:
|
||||
|
||||
```php
|
||||
'locale' => 'en', // Change to your preferred language code
|
||||
```
|
||||
|
||||
### Customizing markdown styles
|
||||
|
||||
Edit `resources/css/app.css`:
|
||||
|
||||
```css
|
||||
@layer components {
|
||||
.prose .wiki-link {
|
||||
@apply text-indigo-600 hover:text-indigo-800 underline;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Livewire assets not loading
|
||||
|
||||
```bash
|
||||
docker compose exec php php artisan livewire:publish --assets
|
||||
```
|
||||
|
||||
### Frontend changes not reflecting
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
docker compose exec php php artisan view:clear
|
||||
```
|
||||
|
||||
### Database connection errors
|
||||
|
||||
Check `.env` file matches Docker Compose settings:
|
||||
|
||||
```env
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=kb_mysql
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=knowledge_base
|
||||
DB_USERNAME=kb_user
|
||||
DB_PASSWORD=kb_password
|
||||
```
|
||||
|
||||
### Alpine.js errors in console
|
||||
|
||||
Ensure scripts are loaded in correct order in `knowledge-base.blade.php`:
|
||||
1. Livewire scripts first
|
||||
2. Alpine.js initialization (via Vite)
|
||||
3. Custom Alpine components
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
Contributions are welcome! Please ensure:
|
||||
- Code follows Laravel and PSR-12 conventions
|
||||
- All existing tests pass
|
||||
- New features include tests
|
||||
- UI changes maintain responsive design
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
This project is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
||||
## Credits
|
||||
|
||||
Built with:
|
||||
- [Laravel](https://laravel.com) - The PHP Framework
|
||||
- [Livewire](https://livewire.laravel.com) - Full-stack framework for Laravel
|
||||
- [Alpine.js](https://alpinejs.dev) - Lightweight JavaScript framework
|
||||
- [Tailwind CSS](https://tailwindcss.com) - Utility-first CSS framework
|
||||
- [EasyMDE](https://github.com/Ionaru/easy-markdown-editor) - Markdown editor
|
||||
- [league/commonmark](https://commonmark.thephpleague.com) - Markdown parser
|
||||
|
||||
@@ -33,7 +33,7 @@ public function store(LoginRequest $request): RedirectResponse
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
|
||||
return redirect()->intended('/');
|
||||
return redirect()->intended(url('/'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,6 +47,6 @@ public function destroy(Request $request): RedirectResponse
|
||||
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
return redirect(url('/'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ class LocaleController extends Controller
|
||||
{
|
||||
/**
|
||||
* Update the user's locale preference.
|
||||
* Works for both authenticated and guest users.
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
@@ -19,12 +20,17 @@ public function update(Request $request)
|
||||
|
||||
$locale = $validated['locale'];
|
||||
|
||||
// Save to user record
|
||||
Auth::user()->update(['locale' => $locale]);
|
||||
|
||||
// Also save to session for immediate effect
|
||||
// Save to session for immediate effect
|
||||
$request->session()->put('locale', $locale);
|
||||
|
||||
return redirect()->route('profile.edit')->with('success', __('messages.settings.language_updated'));
|
||||
// If authenticated, also save to user record for persistence
|
||||
if (Auth::check()) {
|
||||
Auth::user()->update(['locale' => $locale]);
|
||||
}
|
||||
|
||||
// Set long-lived cookie (1 year) for guest users
|
||||
cookie()->queue('locale', $locale, 525600); // 525600 minutes = 1 year
|
||||
|
||||
return redirect()->back()->with('success', __('messages.settings.language_updated'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,6 @@ public function destroy(Request $request): RedirectResponse
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return Redirect::to('/');
|
||||
return Redirect::to(url('/'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,36 +11,64 @@
|
||||
class SetLocale
|
||||
{
|
||||
/**
|
||||
* Supported locales
|
||||
* Supported locales (LTR languages only)
|
||||
* Order: English, Japanese, Chinese, Korean, Other Asian, European
|
||||
*/
|
||||
public const SUPPORTED_LOCALES = [
|
||||
// Primary languages
|
||||
'en' => 'English',
|
||||
'ja' => '日本語',
|
||||
|
||||
// Chinese variants
|
||||
'zh-CN' => '简体中文',
|
||||
'zh-TW' => '繁體中文',
|
||||
|
||||
// Korean
|
||||
'ko' => '한국어',
|
||||
|
||||
// Other Asian languages
|
||||
'hi' => 'हिन्दी',
|
||||
'vi' => 'Tiếng Việt',
|
||||
'tr' => 'Türkçe',
|
||||
|
||||
// European languages
|
||||
'de' => 'Deutsch',
|
||||
'fr' => 'Français',
|
||||
'es' => 'Español',
|
||||
'zh-CN' => '简体中文',
|
||||
'zh-TW' => '繁體中文',
|
||||
'ko' => '한국어',
|
||||
'pt-BR' => 'Português (Brasil)',
|
||||
'ru' => 'Русский',
|
||||
'uk' => 'Українська',
|
||||
'it' => 'Italiano',
|
||||
'pl' => 'Polski',
|
||||
];
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* Priority order:
|
||||
* 1. Authenticated user's database preference
|
||||
* 2. Session (for immediate effect after changing)
|
||||
* 3. Cookie (long-term storage for guest users)
|
||||
* 4. Default locale from config
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$locale = config('app.locale', 'en');
|
||||
|
||||
// Check authenticated user's preference
|
||||
// Priority 1: Check authenticated user's preference
|
||||
if (Auth::check() && Auth::user()->locale) {
|
||||
$locale = Auth::user()->locale;
|
||||
}
|
||||
// Check session (for immediate effect after changing)
|
||||
// Priority 2: Check session (for immediate effect after changing)
|
||||
elseif ($request->session()->has('locale')) {
|
||||
$locale = $request->session()->get('locale');
|
||||
}
|
||||
// Priority 3: Check cookie (long-term storage for guest users)
|
||||
elseif ($request->cookie('locale')) {
|
||||
$locale = $request->cookie('locale');
|
||||
}
|
||||
|
||||
// Validate locale
|
||||
if (!array_key_exists($locale, self::SUPPORTED_LOCALES)) {
|
||||
|
||||
@@ -80,7 +80,7 @@ public function delete(DocumentService $documentService)
|
||||
if ($homeDocument) {
|
||||
return redirect()->route('documents.show', $homeDocument);
|
||||
}
|
||||
return redirect('/');
|
||||
return redirect(url('/'));
|
||||
} catch (\Exception $e) {
|
||||
session()->flash('error', 'Error deleting document: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
@@ -93,9 +93,9 @@ public function selectDocument()
|
||||
if (isset($results[$this->selectedIndex])) {
|
||||
$document = $results[$this->selectedIndex];
|
||||
|
||||
// id が存在することを確認
|
||||
if (!empty($document['id'])) {
|
||||
return $this->redirect(route('documents.show', $document['id']));
|
||||
// slug が存在することを確認
|
||||
if (!empty($document['slug'])) {
|
||||
return $this->redirect(route('documents.show', $document['slug']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,37 @@ class Document extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* Get the route key for the model.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRouteKeyName(): string
|
||||
{
|
||||
return 'slug';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the model for a bound value.
|
||||
* Supports both slug and ID for backwards compatibility.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string|null $field
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
*/
|
||||
public function resolveRouteBinding($value, $field = null)
|
||||
{
|
||||
// First try to find by slug
|
||||
$document = $this->where('slug', $value)->first();
|
||||
|
||||
// If not found by slug, try by ID (for backwards compatibility)
|
||||
if (!$document && is_numeric($value)) {
|
||||
$document = $this->where('id', $value)->first();
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
@@ -154,9 +185,9 @@ function ($matches) {
|
||||
->first();
|
||||
|
||||
if ($targetDocument) {
|
||||
return '<a href="/documents/' . $targetDocument->slug . '" class="wiki-link">' . e($linkTitle) . '</a>';
|
||||
return '<a href="' . route('documents.show', $targetDocument->slug) . '" class="wiki-link">' . e($linkTitle) . '</a>';
|
||||
} else {
|
||||
return '<a href="/documents/create?title=' . urlencode($linkTitle) . '" class="wiki-link wiki-link-new">' . e($linkTitle) . '</a>';
|
||||
return '<a href="' . route('documents.create') . '?title=' . urlencode($linkTitle) . '" class="wiki-link wiki-link-new">' . e($linkTitle) . '</a>';
|
||||
}
|
||||
},
|
||||
$this->rendered_html
|
||||
|
||||
@@ -57,9 +57,9 @@ public function isBroken(): bool
|
||||
public function getUrlAttribute(): string
|
||||
{
|
||||
if ($this->isBroken()) {
|
||||
return '/documents/create?title=' . urlencode($this->target_title);
|
||||
return route('documents.create') . '?title=' . urlencode($this->target_title);
|
||||
}
|
||||
|
||||
return '/documents/' . $this->targetDocument->slug;
|
||||
return route('documents.show', $this->targetDocument->slug);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
'timezone' => env('APP_TIMEZONE', 'Asia/Tokyo'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
125
src/config/livewire.php
Normal file
125
src/config/livewire.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Livewire Assets URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value sets the path to Livewire JavaScript assets, for cases where
|
||||
| your app's domain root is not the correct path. By default, Livewire
|
||||
| will load its JavaScript assets from the app's "relative root".
|
||||
|
|
||||
| Examples: "/assets", "myurl.com/app".
|
||||
|
|
||||
*/
|
||||
|
||||
'asset_url' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Livewire App URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value should be used if livewire assets are served from CDN.
|
||||
| Livewire will communicate with an app through this url.
|
||||
|
|
||||
| Examples: "https://my-app.com", "myurl.com/app".
|
||||
|
|
||||
*/
|
||||
|
||||
'app_url' => env('APP_URL'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Livewire Update Endpoint
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The endpoint for Livewire AJAX requests.
|
||||
|
|
||||
*/
|
||||
|
||||
'update_endpoint' => '/livewire/update',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Livewire Endpoint Middleware Group
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value sets the middleware group that will be applied to the main
|
||||
| Livewire "message" endpoint (the endpoint that gets hit everytime
|
||||
| a Livewire component updates). It is set to "web" by default.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware_group' => 'web',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Livewire Temporary File Uploads Endpoint Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Livewire handles file uploads by storing uploads in a temporary directory
|
||||
| before the file is stored permanently. All file uploads are directed
|
||||
| to a global endpoint for temporary storage. Here you may configure.
|
||||
|
|
||||
*/
|
||||
|
||||
'temporary_file_upload' => [
|
||||
'disk' => null,
|
||||
'rules' => null,
|
||||
'directory' => null,
|
||||
'middleware' => null,
|
||||
'preview_mimes' => [
|
||||
'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
|
||||
'mov', 'avi', 'wmv', 'mp3', 'm4a',
|
||||
'jpg', 'jpeg', 'mpga', 'webp', 'wma',
|
||||
],
|
||||
'max_upload_time' => 5,
|
||||
'cleanup' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Render On Redirect
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines if Livewire will render before it's redirected
|
||||
| or not. Setting it to "false" will mean the render method is skipped
|
||||
| when performing a redirect, potentially improving performances.
|
||||
|
|
||||
*/
|
||||
|
||||
'render_on_redirect' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Navigate (SPA mode)
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, Livewire uses the "replace" strategy for SPA navigation
|
||||
| which can sometimes cause issues with subdirectory deployments.
|
||||
|
|
||||
*/
|
||||
|
||||
'navigate' => [
|
||||
'show_progress_bar' => true,
|
||||
'progress_bar_color' => '#2299dd',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Inject Assets
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, Livewire automatically injects its JavaScript and styles
|
||||
| into the <head> and <body> of your pages. If you want to disable
|
||||
| this behavior and manually include assets, set this to false.
|
||||
|
|
||||
*/
|
||||
|
||||
'inject_assets' => true,
|
||||
|
||||
];
|
||||
|
||||
@@ -115,6 +115,14 @@
|
||||
'already_registered' => 'Bereits registriert?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Seite nicht gefunden',
|
||||
'page_not_found' => 'Seite nicht gefunden',
|
||||
'page_not_found_description' => 'Die gesuchte Seite konnte nicht gefunden werden.',
|
||||
'back_to_home' => 'Zurück zur Startseite',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Profil',
|
||||
|
||||
@@ -115,6 +115,14 @@
|
||||
'already_registered' => 'Already registered?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Page Not Found',
|
||||
'page_not_found' => 'Page Not Found',
|
||||
'page_not_found_description' => 'The page you are looking for could not be found.',
|
||||
'back_to_home' => 'Back to Home',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Profile',
|
||||
|
||||
@@ -115,6 +115,14 @@
|
||||
'already_registered' => '¿Ya está registrado?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Página no encontrada',
|
||||
'page_not_found' => 'Página no encontrada',
|
||||
'page_not_found_description' => 'No se pudo encontrar la página que está buscando.',
|
||||
'back_to_home' => 'Volver al inicio',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Perfil',
|
||||
|
||||
@@ -115,6 +115,14 @@
|
||||
'already_registered' => 'Déjà inscrit ?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Page non trouvée',
|
||||
'page_not_found' => 'Page non trouvée',
|
||||
'page_not_found_description' => 'La page que vous recherchez est introuvable.',
|
||||
'back_to_home' => 'Retour à l\'accueil',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Profil',
|
||||
|
||||
148
src/lang/hi/messages.php
Normal file
148
src/lang/hi/messages.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Navigation
|
||||
'nav' => [
|
||||
'dashboard' => 'डैशबोर्ड',
|
||||
'knowledge_base' => 'ज्ञान आधार',
|
||||
'profile' => 'प्रोफ़ाइल',
|
||||
'user_management' => 'उपयोगकर्ता प्रबंधन',
|
||||
'logout' => 'लॉग आउट',
|
||||
'login' => 'लॉगिन',
|
||||
'register' => 'रजिस्टर',
|
||||
],
|
||||
|
||||
// Documents
|
||||
'documents' => [
|
||||
'title' => 'दस्तावेज़',
|
||||
'new_document' => 'नया दस्तावेज़',
|
||||
'edit_document' => 'दस्तावेज़ संपादित करें',
|
||||
'edit' => 'संपादित करें',
|
||||
'delete' => 'हटाएं',
|
||||
'save' => 'सहेजें',
|
||||
'cancel' => 'रद्द करें',
|
||||
'created_by' => 'द्वारा बनाया गया',
|
||||
'modified_by' => 'द्वारा',
|
||||
'updated' => 'अपडेट किया गया',
|
||||
'path' => 'पथ',
|
||||
'last_modified' => 'अंतिम संशोधन',
|
||||
'no_documents' => 'कोई दस्तावेज़ नहीं मिला',
|
||||
'search_placeholder' => 'दस्तावेज़ खोजें...',
|
||||
'create_success' => 'दस्तावेज़ सफलतापूर्वक बनाया गया!',
|
||||
'update_success' => 'दस्तावेज़ सफलतापूर्वक अपडेट किया गया!',
|
||||
'delete_success' => 'दस्तावेज़ सफलतापूर्वक हटाया गया!',
|
||||
'delete_confirm' => 'क्या आप वाकई इस दस्तावेज़ को हटाना चाहते हैं?',
|
||||
'linked_references' => 'लिंक किए गए संदर्भ',
|
||||
'title_label' => 'शीर्षक',
|
||||
'title_placeholder' => 'दस्तावेज़ शीर्षक (फ़ोल्डर के लिए / का उपयोग करें, जैसे Laravel/Livewire/Components)',
|
||||
'title_hint' => 'सुझाव: दस्तावेज़ों को स्वचालित रूप से फ़ोल्डर में व्यवस्थित करने के लिए शीर्षक में स्लैश (/) का उपयोग करें',
|
||||
'content_label' => 'सामग्री',
|
||||
'content_placeholder' => 'यहां अपना मार्कडाउन लिखें...',
|
||||
'saving' => 'सहेजा जा रहा है...',
|
||||
],
|
||||
|
||||
// Quick Switcher
|
||||
'quick_switcher' => [
|
||||
'title' => 'त्वरित स्विच',
|
||||
'placeholder' => 'दस्तावेज़ खोजें...',
|
||||
'no_results' => 'कोई दस्तावेज़ नहीं मिला',
|
||||
'navigate' => 'नेविगेट करने के लिए',
|
||||
'select' => 'चुनने के लिए',
|
||||
'close' => 'बंद करने के लिए',
|
||||
],
|
||||
|
||||
// Admin
|
||||
'admin' => [
|
||||
'user_management' => 'उपयोगकर्ता प्रबंधन',
|
||||
'new_user' => 'नया उपयोगकर्ता',
|
||||
'edit_user' => 'उपयोगकर्ता संपादित करें',
|
||||
'create_user' => 'उपयोगकर्ता बनाएं',
|
||||
'users' => 'उपयोगकर्ता',
|
||||
'name' => 'नाम',
|
||||
'email' => 'ईमेल',
|
||||
'password' => 'पासवर्ड',
|
||||
'password_confirmation' => 'पासवर्ड की पुष्टि करें',
|
||||
'password_hint' => 'वर्तमान पासवर्ड रखने के लिए खाली छोड़ दें।',
|
||||
'role' => 'भूमिका',
|
||||
'admin' => 'प्रशासक',
|
||||
'user' => 'उपयोगकर्ता',
|
||||
'grant_admin' => 'प्रशासक विशेषाधिकार प्रदान करें',
|
||||
'created_at' => 'बनाया गया',
|
||||
'actions' => 'क्रियाएं',
|
||||
'edit' => 'संपादित करें',
|
||||
'delete' => 'हटाएं',
|
||||
'no_users' => 'कोई उपयोगकर्ता नहीं मिला।',
|
||||
'create_success' => 'उपयोगकर्ता सफलतापूर्वक बनाया गया।',
|
||||
'update_success' => 'उपयोगकर्ता सफलतापूर्वक अपडेट किया गया।',
|
||||
'delete_success' => 'उपयोगकर्ता सफलतापूर्वक हटाया गया।',
|
||||
'cannot_delete_self' => 'आप स्वयं को हटा नहीं सकते।',
|
||||
'self_admin_warning' => 'अपने स्वयं के प्रशासक विशेषाधिकारों को हटाने से आपका प्रशासन पैनल तक पहुंच अवरुद्ध हो जाएगी।',
|
||||
],
|
||||
|
||||
// Settings
|
||||
'settings' => [
|
||||
'language' => 'भाषा',
|
||||
'select_language' => 'भाषा चुनें',
|
||||
'language_updated' => 'भाषा सफलतापूर्वक अपडेट की गई।',
|
||||
'change_language' => 'भाषा बदलें',
|
||||
],
|
||||
|
||||
// Common
|
||||
'common' => [
|
||||
'save' => 'सहेजें',
|
||||
'cancel' => 'रद्द करें',
|
||||
'delete' => 'हटाएं',
|
||||
'edit' => 'संपादित करें',
|
||||
'create' => 'बनाएं',
|
||||
'update' => 'अपडेट करें',
|
||||
'back' => 'वापस',
|
||||
'confirm' => 'पुष्टि करें',
|
||||
'yes' => 'हां',
|
||||
'no' => 'नहीं',
|
||||
'loading' => 'लोड हो रहा है...',
|
||||
'error' => 'त्रुटि',
|
||||
'success' => 'सफलता',
|
||||
],
|
||||
|
||||
// Auth
|
||||
'auth' => [
|
||||
'login' => 'लॉगिन',
|
||||
'register' => 'रजिस्टर',
|
||||
'email' => 'ईमेल',
|
||||
'password' => 'पासवर्ड',
|
||||
'remember_me' => 'मुझे याद रखें',
|
||||
'forgot_password' => 'पासवर्ड भूल गए?',
|
||||
'confirm_password' => 'पासवर्ड की पुष्टि करें',
|
||||
'already_registered' => 'पहले से रजिस्टर्ड?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'पेज नहीं मिला',
|
||||
'page_not_found' => 'पेज नहीं मिला',
|
||||
'page_not_found_description' => 'आप जिस पेज की तलाश कर रहे हैं वह नहीं मिला।',
|
||||
'back_to_home' => 'होम पर वापस जाएं',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'प्रोफ़ाइल',
|
||||
'information' => 'प्रोफ़ाइल जानकारी',
|
||||
'information_description' => 'अपने खाते की प्रोफ़ाइल जानकारी और ईमेल पता अपडेट करें।',
|
||||
'name' => 'नाम',
|
||||
'email' => 'ईमेल',
|
||||
'email_unverified' => 'आपका ईमेल पता सत्यापित नहीं है।',
|
||||
'resend_verification' => 'सत्यापन ईमेल फिर से भेजने के लिए यहां क्लिक करें।',
|
||||
'verification_sent' => 'आपके ईमेल पते पर एक नया सत्यापन लिंक भेजा गया है।',
|
||||
'saved' => 'सहेजा गया।',
|
||||
'update_password' => 'पासवर्ड अपडेट करें',
|
||||
'update_password_description' => 'सुनिश्चित करें कि आपका खाता सुरक्षित रहने के लिए लंबे, यादृच्छिक पासवर्ड का उपयोग कर रहा है।',
|
||||
'current_password' => 'वर्तमान पासवर्ड',
|
||||
'new_password' => 'नया पासवर्ड',
|
||||
'confirm_password' => 'पासवर्ड की पुष्टि करें',
|
||||
'delete_account' => 'खाता हटाएं',
|
||||
'delete_account_description' => 'एक बार आपका खाता हटा दिए जाने के बाद, इसके सभी संसाधन और डेटा स्थायी रूप से हटा दिए जाएंगे। अपना खाता हटाने से पहले, कृपया कोई भी डेटा या जानकारी डाउनलोड करें जिसे आप बनाए रखना चाहते हैं।',
|
||||
'delete_account_confirm' => 'क्या आप वाकई अपना खाता हटाना चाहते हैं?',
|
||||
'delete_account_confirm_description' => 'एक बार आपका खाता हटा दिए जाने के बाद, इसके सभी संसाधन और डेटा स्थायी रूप से हटा दिए जाएंगे। कृपया पुष्टि करने के लिए अपना पासवर्ड दर्ज करें कि आप स्थायी रूप से अपना खाता हटाना चाहते हैं।',
|
||||
],
|
||||
];
|
||||
148
src/lang/it/messages.php
Normal file
148
src/lang/it/messages.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Navigation
|
||||
'nav' => [
|
||||
'dashboard' => 'Pannello',
|
||||
'knowledge_base' => 'Base di Conoscenza',
|
||||
'profile' => 'Profilo',
|
||||
'user_management' => 'Gestione Utenti',
|
||||
'logout' => 'Esci',
|
||||
'login' => 'Accedi',
|
||||
'register' => 'Registrati',
|
||||
],
|
||||
|
||||
// Documents
|
||||
'documents' => [
|
||||
'title' => 'Documenti',
|
||||
'new_document' => 'Nuovo Documento',
|
||||
'edit_document' => 'Modifica Documento',
|
||||
'edit' => 'Modifica',
|
||||
'delete' => 'Elimina',
|
||||
'save' => 'Salva',
|
||||
'cancel' => 'Annulla',
|
||||
'created_by' => 'Creato da',
|
||||
'modified_by' => 'da',
|
||||
'updated' => 'Aggiornato',
|
||||
'path' => 'Percorso',
|
||||
'last_modified' => 'Ultima modifica',
|
||||
'no_documents' => 'Nessun documento trovato',
|
||||
'search_placeholder' => 'Cerca documenti...',
|
||||
'create_success' => 'Documento creato con successo!',
|
||||
'update_success' => 'Documento aggiornato con successo!',
|
||||
'delete_success' => 'Documento eliminato con successo!',
|
||||
'delete_confirm' => 'Sei sicuro di voler eliminare questo documento?',
|
||||
'linked_references' => 'Riferimenti Collegati',
|
||||
'title_label' => 'Titolo',
|
||||
'title_placeholder' => 'Titolo del Documento (usa / per le cartelle, es: Laravel/Livewire/Components)',
|
||||
'title_hint' => 'Suggerimento: Usa le barre (/) nel titolo per organizzare automaticamente i documenti in cartelle',
|
||||
'content_label' => 'Contenuto',
|
||||
'content_placeholder' => 'Scrivi il tuo markdown qui...',
|
||||
'saving' => 'Salvataggio...',
|
||||
],
|
||||
|
||||
// Quick Switcher
|
||||
'quick_switcher' => [
|
||||
'title' => 'Cambio Rapido',
|
||||
'placeholder' => 'Cerca documenti...',
|
||||
'no_results' => 'Nessun documento trovato',
|
||||
'navigate' => 'per navigare',
|
||||
'select' => 'per selezionare',
|
||||
'close' => 'per chiudere',
|
||||
],
|
||||
|
||||
// Admin
|
||||
'admin' => [
|
||||
'user_management' => 'Gestione Utenti',
|
||||
'new_user' => 'Nuovo Utente',
|
||||
'edit_user' => 'Modifica Utente',
|
||||
'create_user' => 'Crea Utente',
|
||||
'users' => 'Utenti',
|
||||
'name' => 'Nome',
|
||||
'email' => 'Email',
|
||||
'password' => 'Password',
|
||||
'password_confirmation' => 'Conferma Password',
|
||||
'password_hint' => 'Lascia vuoto per mantenere la password attuale.',
|
||||
'role' => 'Ruolo',
|
||||
'admin' => 'Amministratore',
|
||||
'user' => 'Utente',
|
||||
'grant_admin' => 'Concedi privilegi di amministratore',
|
||||
'created_at' => 'Creato',
|
||||
'actions' => 'Azioni',
|
||||
'edit' => 'Modifica',
|
||||
'delete' => 'Elimina',
|
||||
'no_users' => 'Nessun utente trovato.',
|
||||
'create_success' => 'Utente creato con successo.',
|
||||
'update_success' => 'Utente aggiornato con successo.',
|
||||
'delete_success' => 'Utente eliminato con successo.',
|
||||
'cannot_delete_self' => 'Non puoi eliminare te stesso.',
|
||||
'self_admin_warning' => 'Rimuovere i propri privilegi di amministratore bloccherà il tuo accesso al pannello di amministrazione.',
|
||||
],
|
||||
|
||||
// Settings
|
||||
'settings' => [
|
||||
'language' => 'Lingua',
|
||||
'select_language' => 'Seleziona Lingua',
|
||||
'language_updated' => 'Lingua aggiornata con successo.',
|
||||
'change_language' => 'Cambia lingua',
|
||||
],
|
||||
|
||||
// Common
|
||||
'common' => [
|
||||
'save' => 'Salva',
|
||||
'cancel' => 'Annulla',
|
||||
'delete' => 'Elimina',
|
||||
'edit' => 'Modifica',
|
||||
'create' => 'Crea',
|
||||
'update' => 'Aggiorna',
|
||||
'back' => 'Indietro',
|
||||
'confirm' => 'Conferma',
|
||||
'yes' => 'Sì',
|
||||
'no' => 'No',
|
||||
'loading' => 'Caricamento...',
|
||||
'error' => 'Errore',
|
||||
'success' => 'Successo',
|
||||
],
|
||||
|
||||
// Auth
|
||||
'auth' => [
|
||||
'login' => 'Accedi',
|
||||
'register' => 'Registrati',
|
||||
'email' => 'Email',
|
||||
'password' => 'Password',
|
||||
'remember_me' => 'Ricordami',
|
||||
'forgot_password' => 'Password dimenticata?',
|
||||
'confirm_password' => 'Conferma Password',
|
||||
'already_registered' => 'Già registrato?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Pagina Non Trovata',
|
||||
'page_not_found' => 'Pagina Non Trovata',
|
||||
'page_not_found_description' => 'La pagina che stai cercando non è stata trovata.',
|
||||
'back_to_home' => 'Torna alla Home',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Profilo',
|
||||
'information' => 'Informazioni Profilo',
|
||||
'information_description' => 'Aggiorna le informazioni del profilo e l\'indirizzo email del tuo account.',
|
||||
'name' => 'Nome',
|
||||
'email' => 'Email',
|
||||
'email_unverified' => 'Il tuo indirizzo email non è verificato.',
|
||||
'resend_verification' => 'Clicca qui per inviare nuovamente l\'email di verifica.',
|
||||
'verification_sent' => 'Un nuovo link di verifica è stato inviato al tuo indirizzo email.',
|
||||
'saved' => 'Salvato.',
|
||||
'update_password' => 'Aggiorna Password',
|
||||
'update_password_description' => 'Assicurati che il tuo account utilizzi una password lunga e casuale per rimanere sicuro.',
|
||||
'current_password' => 'Password Attuale',
|
||||
'new_password' => 'Nuova Password',
|
||||
'confirm_password' => 'Conferma Password',
|
||||
'delete_account' => 'Elimina Account',
|
||||
'delete_account_description' => 'Una volta eliminato il tuo account, tutte le sue risorse e dati saranno cancellati permanentemente. Prima di eliminare il tuo account, scarica eventuali dati o informazioni che desideri conservare.',
|
||||
'delete_account_confirm' => 'Sei sicuro di voler eliminare il tuo account?',
|
||||
'delete_account_confirm_description' => 'Una volta eliminato il tuo account, tutte le sue risorse e dati saranno cancellati permanentemente. Inserisci la tua password per confermare che desideri eliminare permanentemente il tuo account.',
|
||||
],
|
||||
];
|
||||
@@ -115,6 +115,14 @@
|
||||
'already_registered' => '既にアカウントをお持ちですか?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'ページが見つかりません',
|
||||
'page_not_found' => 'ページが見つかりません',
|
||||
'page_not_found_description' => 'お探しのページは見つかりませんでした。',
|
||||
'back_to_home' => 'ホームに戻る',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'プロフィール',
|
||||
|
||||
@@ -115,6 +115,14 @@
|
||||
'already_registered' => '이미 계정이 있으신가요?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => '페이지를 찾을 수 없습니다',
|
||||
'page_not_found' => '페이지를 찾을 수 없습니다',
|
||||
'page_not_found_description' => '찾고 계신 페이지를 찾을 수 없습니다.',
|
||||
'back_to_home' => '홈으로 돌아가기',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => '프로필',
|
||||
|
||||
148
src/lang/pl/messages.php
Normal file
148
src/lang/pl/messages.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Navigation
|
||||
'nav' => [
|
||||
'dashboard' => 'Panel',
|
||||
'knowledge_base' => 'Baza wiedzy',
|
||||
'profile' => 'Profil',
|
||||
'user_management' => 'Zarządzanie użytkownikami',
|
||||
'logout' => 'Wyloguj',
|
||||
'login' => 'Zaloguj',
|
||||
'register' => 'Zarejestruj',
|
||||
],
|
||||
|
||||
// Documents
|
||||
'documents' => [
|
||||
'title' => 'Dokumenty',
|
||||
'new_document' => 'Nowy dokument',
|
||||
'edit_document' => 'Edytuj dokument',
|
||||
'edit' => 'Edytuj',
|
||||
'delete' => 'Usuń',
|
||||
'save' => 'Zapisz',
|
||||
'cancel' => 'Anuluj',
|
||||
'created_by' => 'Utworzony przez',
|
||||
'modified_by' => 'przez',
|
||||
'updated' => 'Zaktualizowano',
|
||||
'path' => 'Ścieżka',
|
||||
'last_modified' => 'Ostatnia modyfikacja',
|
||||
'no_documents' => 'Nie znaleziono dokumentów',
|
||||
'search_placeholder' => 'Szukaj dokumentów...',
|
||||
'create_success' => 'Dokument został utworzony!',
|
||||
'update_success' => 'Dokument został zaktualizowany!',
|
||||
'delete_success' => 'Dokument został usunięty!',
|
||||
'delete_confirm' => 'Czy na pewno chcesz usunąć ten dokument?',
|
||||
'linked_references' => 'Powiązane odniesienia',
|
||||
'title_label' => 'Tytuł',
|
||||
'title_placeholder' => 'Tytuł dokumentu (użyj / dla folderów, np. Laravel/Livewire/Components)',
|
||||
'title_hint' => 'Wskazówka: Użyj ukośników (/) w tytule, aby automatycznie organizować dokumenty w foldery',
|
||||
'content_label' => 'Treść',
|
||||
'content_placeholder' => 'Napisz swój markdown tutaj...',
|
||||
'saving' => 'Zapisywanie...',
|
||||
],
|
||||
|
||||
// Quick Switcher
|
||||
'quick_switcher' => [
|
||||
'title' => 'Szybkie przełączanie',
|
||||
'placeholder' => 'Szukaj dokumentów...',
|
||||
'no_results' => 'Nie znaleziono dokumentów',
|
||||
'navigate' => 'aby nawigować',
|
||||
'select' => 'aby wybrać',
|
||||
'close' => 'aby zamknąć',
|
||||
],
|
||||
|
||||
// Admin
|
||||
'admin' => [
|
||||
'user_management' => 'Zarządzanie użytkownikami',
|
||||
'new_user' => 'Nowy użytkownik',
|
||||
'edit_user' => 'Edytuj użytkownika',
|
||||
'create_user' => 'Utwórz użytkownika',
|
||||
'users' => 'Użytkownicy',
|
||||
'name' => 'Nazwa',
|
||||
'email' => 'E-mail',
|
||||
'password' => 'Hasło',
|
||||
'password_confirmation' => 'Potwierdź hasło',
|
||||
'password_hint' => 'Pozostaw puste, aby zachować obecne hasło.',
|
||||
'role' => 'Rola',
|
||||
'admin' => 'Administrator',
|
||||
'user' => 'Użytkownik',
|
||||
'grant_admin' => 'Przyznaj uprawnienia administratora',
|
||||
'created_at' => 'Utworzono',
|
||||
'actions' => 'Działania',
|
||||
'edit' => 'Edytuj',
|
||||
'delete' => 'Usuń',
|
||||
'no_users' => 'Nie znaleziono użytkowników.',
|
||||
'create_success' => 'Użytkownik został utworzony.',
|
||||
'update_success' => 'Użytkownik został zaktualizowany.',
|
||||
'delete_success' => 'Użytkownik został usunięty.',
|
||||
'cannot_delete_self' => 'Nie możesz usunąć samego siebie.',
|
||||
'self_admin_warning' => 'Usunięcie własnych uprawnień administratora zablokuje Twój dostęp do panelu administratora.',
|
||||
],
|
||||
|
||||
// Settings
|
||||
'settings' => [
|
||||
'language' => 'Język',
|
||||
'select_language' => 'Wybierz język',
|
||||
'language_updated' => 'Język został zaktualizowany.',
|
||||
'change_language' => 'Zmień język',
|
||||
],
|
||||
|
||||
// Common
|
||||
'common' => [
|
||||
'save' => 'Zapisz',
|
||||
'cancel' => 'Anuluj',
|
||||
'delete' => 'Usuń',
|
||||
'edit' => 'Edytuj',
|
||||
'create' => 'Utwórz',
|
||||
'update' => 'Aktualizuj',
|
||||
'back' => 'Wstecz',
|
||||
'confirm' => 'Potwierdź',
|
||||
'yes' => 'Tak',
|
||||
'no' => 'Nie',
|
||||
'loading' => 'Ładowanie...',
|
||||
'error' => 'Błąd',
|
||||
'success' => 'Sukces',
|
||||
],
|
||||
|
||||
// Auth
|
||||
'auth' => [
|
||||
'login' => 'Zaloguj',
|
||||
'register' => 'Zarejestruj',
|
||||
'email' => 'E-mail',
|
||||
'password' => 'Hasło',
|
||||
'remember_me' => 'Zapamiętaj mnie',
|
||||
'forgot_password' => 'Zapomniałeś hasła?',
|
||||
'confirm_password' => 'Potwierdź hasło',
|
||||
'already_registered' => 'Już zarejestrowany?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Strona nie znaleziona',
|
||||
'page_not_found' => 'Strona nie znaleziona',
|
||||
'page_not_found_description' => 'Strona, której szukasz, nie została znaleziona.',
|
||||
'back_to_home' => 'Wróć do strony głównej',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Profil',
|
||||
'information' => 'Informacje o profilu',
|
||||
'information_description' => 'Zaktualizuj informacje o profilu i adres e-mail swojego konta.',
|
||||
'name' => 'Nazwa',
|
||||
'email' => 'E-mail',
|
||||
'email_unverified' => 'Twój adres e-mail nie został zweryfikowany.',
|
||||
'resend_verification' => 'Kliknij tutaj, aby ponownie wysłać e-mail weryfikacyjny.',
|
||||
'verification_sent' => 'Nowy link weryfikacyjny został wysłany na Twój adres e-mail.',
|
||||
'saved' => 'Zapisano.',
|
||||
'update_password' => 'Zaktualizuj hasło',
|
||||
'update_password_description' => 'Upewnij się, że Twoje konto używa długiego, losowego hasła, aby pozostać bezpiecznym.',
|
||||
'current_password' => 'Obecne hasło',
|
||||
'new_password' => 'Nowe hasło',
|
||||
'confirm_password' => 'Potwierdź hasło',
|
||||
'delete_account' => 'Usuń konto',
|
||||
'delete_account_description' => 'Po usunięciu konta wszystkie jego zasoby i dane zostaną trwale usunięte. Przed usunięciem konta pobierz wszelkie dane lub informacje, które chcesz zachować.',
|
||||
'delete_account_confirm' => 'Czy na pewno chcesz usunąć swoje konto?',
|
||||
'delete_account_confirm_description' => 'Po usunięciu konta wszystkie jego zasoby i dane zostaną trwale usunięte. Wprowadź swoje hasło, aby potwierdzić, że chcesz trwale usunąć swoje konto.',
|
||||
],
|
||||
];
|
||||
148
src/lang/pt-BR/messages.php
Normal file
148
src/lang/pt-BR/messages.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Navigation
|
||||
'nav' => [
|
||||
'dashboard' => 'Painel',
|
||||
'knowledge_base' => 'Base de Conhecimento',
|
||||
'profile' => 'Perfil',
|
||||
'user_management' => 'Gerenciamento de Usuários',
|
||||
'logout' => 'Sair',
|
||||
'login' => 'Entrar',
|
||||
'register' => 'Registrar',
|
||||
],
|
||||
|
||||
// Documents
|
||||
'documents' => [
|
||||
'title' => 'Documentos',
|
||||
'new_document' => 'Novo Documento',
|
||||
'edit_document' => 'Editar Documento',
|
||||
'edit' => 'Editar',
|
||||
'delete' => 'Excluir',
|
||||
'save' => 'Salvar',
|
||||
'cancel' => 'Cancelar',
|
||||
'created_by' => 'Criado por',
|
||||
'modified_by' => 'por',
|
||||
'updated' => 'Atualizado',
|
||||
'path' => 'Caminho',
|
||||
'last_modified' => 'Última modificação',
|
||||
'no_documents' => 'Nenhum documento encontrado',
|
||||
'search_placeholder' => 'Pesquisar documentos...',
|
||||
'create_success' => 'Documento criado com sucesso!',
|
||||
'update_success' => 'Documento atualizado com sucesso!',
|
||||
'delete_success' => 'Documento excluído com sucesso!',
|
||||
'delete_confirm' => 'Tem certeza de que deseja excluir este documento?',
|
||||
'linked_references' => 'Referências Vinculadas',
|
||||
'title_label' => 'Título',
|
||||
'title_placeholder' => 'Título do Documento (use / para pastas, ex: Laravel/Livewire/Components)',
|
||||
'title_hint' => 'Dica: Use barras (/) no título para organizar documentos em pastas automaticamente',
|
||||
'content_label' => 'Conteúdo',
|
||||
'content_placeholder' => 'Escreva seu markdown aqui...',
|
||||
'saving' => 'Salvando...',
|
||||
],
|
||||
|
||||
// Quick Switcher
|
||||
'quick_switcher' => [
|
||||
'title' => 'Troca Rápida',
|
||||
'placeholder' => 'Pesquisar documentos...',
|
||||
'no_results' => 'Nenhum documento encontrado',
|
||||
'navigate' => 'para navegar',
|
||||
'select' => 'para selecionar',
|
||||
'close' => 'para fechar',
|
||||
],
|
||||
|
||||
// Admin
|
||||
'admin' => [
|
||||
'user_management' => 'Gerenciamento de Usuários',
|
||||
'new_user' => 'Novo Usuário',
|
||||
'edit_user' => 'Editar Usuário',
|
||||
'create_user' => 'Criar Usuário',
|
||||
'users' => 'Usuários',
|
||||
'name' => 'Nome',
|
||||
'email' => 'E-mail',
|
||||
'password' => 'Senha',
|
||||
'password_confirmation' => 'Confirmar Senha',
|
||||
'password_hint' => 'Deixe em branco para manter a senha atual.',
|
||||
'role' => 'Função',
|
||||
'admin' => 'Administrador',
|
||||
'user' => 'Usuário',
|
||||
'grant_admin' => 'Conceder privilégios de administrador',
|
||||
'created_at' => 'Criado',
|
||||
'actions' => 'Ações',
|
||||
'edit' => 'Editar',
|
||||
'delete' => 'Excluir',
|
||||
'no_users' => 'Nenhum usuário encontrado.',
|
||||
'create_success' => 'Usuário criado com sucesso.',
|
||||
'update_success' => 'Usuário atualizado com sucesso.',
|
||||
'delete_success' => 'Usuário excluído com sucesso.',
|
||||
'cannot_delete_self' => 'Você não pode excluir a si mesmo.',
|
||||
'self_admin_warning' => 'Remover seus próprios privilégios de administrador bloqueará seu acesso ao painel de administração.',
|
||||
],
|
||||
|
||||
// Settings
|
||||
'settings' => [
|
||||
'language' => 'Idioma',
|
||||
'select_language' => 'Selecionar Idioma',
|
||||
'language_updated' => 'Idioma atualizado com sucesso.',
|
||||
'change_language' => 'Alterar idioma',
|
||||
],
|
||||
|
||||
// Common
|
||||
'common' => [
|
||||
'save' => 'Salvar',
|
||||
'cancel' => 'Cancelar',
|
||||
'delete' => 'Excluir',
|
||||
'edit' => 'Editar',
|
||||
'create' => 'Criar',
|
||||
'update' => 'Atualizar',
|
||||
'back' => 'Voltar',
|
||||
'confirm' => 'Confirmar',
|
||||
'yes' => 'Sim',
|
||||
'no' => 'Não',
|
||||
'loading' => 'Carregando...',
|
||||
'error' => 'Erro',
|
||||
'success' => 'Sucesso',
|
||||
],
|
||||
|
||||
// Auth
|
||||
'auth' => [
|
||||
'login' => 'Entrar',
|
||||
'register' => 'Registrar',
|
||||
'email' => 'E-mail',
|
||||
'password' => 'Senha',
|
||||
'remember_me' => 'Lembrar-me',
|
||||
'forgot_password' => 'Esqueceu sua senha?',
|
||||
'confirm_password' => 'Confirmar Senha',
|
||||
'already_registered' => 'Já registrado?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Página Não Encontrada',
|
||||
'page_not_found' => 'Página Não Encontrada',
|
||||
'page_not_found_description' => 'A página que você está procurando não pôde ser encontrada.',
|
||||
'back_to_home' => 'Voltar para Início',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Perfil',
|
||||
'information' => 'Informações do Perfil',
|
||||
'information_description' => 'Atualize as informações do perfil e endereço de e-mail da sua conta.',
|
||||
'name' => 'Nome',
|
||||
'email' => 'E-mail',
|
||||
'email_unverified' => 'Seu endereço de e-mail não está verificado.',
|
||||
'resend_verification' => 'Clique aqui para reenviar o e-mail de verificação.',
|
||||
'verification_sent' => 'Um novo link de verificação foi enviado para seu endereço de e-mail.',
|
||||
'saved' => 'Salvo.',
|
||||
'update_password' => 'Atualizar Senha',
|
||||
'update_password_description' => 'Certifique-se de que sua conta esteja usando uma senha longa e aleatória para se manter segura.',
|
||||
'current_password' => 'Senha Atual',
|
||||
'new_password' => 'Nova Senha',
|
||||
'confirm_password' => 'Confirmar Senha',
|
||||
'delete_account' => 'Excluir Conta',
|
||||
'delete_account_description' => 'Uma vez que sua conta seja excluída, todos os seus recursos e dados serão permanentemente excluídos. Antes de excluir sua conta, faça o download de quaisquer dados ou informações que deseja reter.',
|
||||
'delete_account_confirm' => 'Tem certeza de que deseja excluir sua conta?',
|
||||
'delete_account_confirm_description' => 'Uma vez que sua conta seja excluída, todos os seus recursos e dados serão permanentemente excluídos. Por favor, insira sua senha para confirmar que deseja excluir permanentemente sua conta.',
|
||||
],
|
||||
];
|
||||
148
src/lang/ru/messages.php
Normal file
148
src/lang/ru/messages.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Navigation
|
||||
'nav' => [
|
||||
'dashboard' => 'Панель управления',
|
||||
'knowledge_base' => 'База знаний',
|
||||
'profile' => 'Профиль',
|
||||
'user_management' => 'Управление пользователями',
|
||||
'logout' => 'Выйти',
|
||||
'login' => 'Войти',
|
||||
'register' => 'Регистрация',
|
||||
],
|
||||
|
||||
// Documents
|
||||
'documents' => [
|
||||
'title' => 'Документы',
|
||||
'new_document' => 'Новый документ',
|
||||
'edit_document' => 'Редактировать документ',
|
||||
'edit' => 'Редактировать',
|
||||
'delete' => 'Удалить',
|
||||
'save' => 'Сохранить',
|
||||
'cancel' => 'Отмена',
|
||||
'created_by' => 'Создано',
|
||||
'modified_by' => '',
|
||||
'updated' => 'Обновлено',
|
||||
'path' => 'Путь',
|
||||
'last_modified' => 'Последнее изменение',
|
||||
'no_documents' => 'Документы не найдены',
|
||||
'search_placeholder' => 'Поиск документов...',
|
||||
'create_success' => 'Документ успешно создан!',
|
||||
'update_success' => 'Документ успешно обновлён!',
|
||||
'delete_success' => 'Документ успешно удалён!',
|
||||
'delete_confirm' => 'Вы уверены, что хотите удалить этот документ?',
|
||||
'linked_references' => 'Связанные ссылки',
|
||||
'title_label' => 'Название',
|
||||
'title_placeholder' => 'Название документа (используйте / для папок, например Laravel/Livewire/Components)',
|
||||
'title_hint' => 'Совет: Используйте слэши (/) в названии для автоматической организации документов в папки',
|
||||
'content_label' => 'Содержимое',
|
||||
'content_placeholder' => 'Напишите здесь ваш markdown...',
|
||||
'saving' => 'Сохранение...',
|
||||
],
|
||||
|
||||
// Quick Switcher
|
||||
'quick_switcher' => [
|
||||
'title' => 'Быстрый переход',
|
||||
'placeholder' => 'Поиск документов...',
|
||||
'no_results' => 'Документы не найдены',
|
||||
'navigate' => 'для навигации',
|
||||
'select' => 'для выбора',
|
||||
'close' => 'для закрытия',
|
||||
],
|
||||
|
||||
// Admin
|
||||
'admin' => [
|
||||
'user_management' => 'Управление пользователями',
|
||||
'new_user' => 'Новый пользователь',
|
||||
'edit_user' => 'Редактировать пользователя',
|
||||
'create_user' => 'Создать пользователя',
|
||||
'users' => 'Пользователи',
|
||||
'name' => 'Имя',
|
||||
'email' => 'Email',
|
||||
'password' => 'Пароль',
|
||||
'password_confirmation' => 'Подтверждение пароля',
|
||||
'password_hint' => 'Оставьте пустым, чтобы сохранить текущий пароль.',
|
||||
'role' => 'Роль',
|
||||
'admin' => 'Администратор',
|
||||
'user' => 'Пользователь',
|
||||
'grant_admin' => 'Предоставить права администратора',
|
||||
'created_at' => 'Создан',
|
||||
'actions' => 'Действия',
|
||||
'edit' => 'Редактировать',
|
||||
'delete' => 'Удалить',
|
||||
'no_users' => 'Пользователи не найдены.',
|
||||
'create_success' => 'Пользователь успешно создан.',
|
||||
'update_success' => 'Пользователь успешно обновлён.',
|
||||
'delete_success' => 'Пользователь успешно удалён.',
|
||||
'cannot_delete_self' => 'Вы не можете удалить себя.',
|
||||
'self_admin_warning' => 'Снятие собственных прав администратора заблокирует вам доступ к панели администратора.',
|
||||
],
|
||||
|
||||
// Settings
|
||||
'settings' => [
|
||||
'language' => 'Язык',
|
||||
'select_language' => 'Выбрать язык',
|
||||
'language_updated' => 'Язык успешно обновлён.',
|
||||
'change_language' => 'Изменить язык',
|
||||
],
|
||||
|
||||
// Common
|
||||
'common' => [
|
||||
'save' => 'Сохранить',
|
||||
'cancel' => 'Отмена',
|
||||
'delete' => 'Удалить',
|
||||
'edit' => 'Редактировать',
|
||||
'create' => 'Создать',
|
||||
'update' => 'Обновить',
|
||||
'back' => 'Назад',
|
||||
'confirm' => 'Подтвердить',
|
||||
'yes' => 'Да',
|
||||
'no' => 'Нет',
|
||||
'loading' => 'Загрузка...',
|
||||
'error' => 'Ошибка',
|
||||
'success' => 'Успех',
|
||||
],
|
||||
|
||||
// Auth
|
||||
'auth' => [
|
||||
'login' => 'Войти',
|
||||
'register' => 'Регистрация',
|
||||
'email' => 'Email',
|
||||
'password' => 'Пароль',
|
||||
'remember_me' => 'Запомнить меня',
|
||||
'forgot_password' => 'Забыли пароль?',
|
||||
'confirm_password' => 'Подтверждение пароля',
|
||||
'already_registered' => 'Уже зарегистрированы?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Страница не найдена',
|
||||
'page_not_found' => 'Страница не найдена',
|
||||
'page_not_found_description' => 'Страница, которую вы ищете, не найдена.',
|
||||
'back_to_home' => 'Вернуться на главную',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Профиль',
|
||||
'information' => 'Информация профиля',
|
||||
'information_description' => 'Обновите информацию профиля и адрес электронной почты вашей учётной записи.',
|
||||
'name' => 'Имя',
|
||||
'email' => 'Email',
|
||||
'email_unverified' => 'Ваш адрес электронной почты не подтверждён.',
|
||||
'resend_verification' => 'Нажмите здесь, чтобы отправить письмо с подтверждением повторно.',
|
||||
'verification_sent' => 'Новая ссылка для подтверждения отправлена на ваш адрес электронной почты.',
|
||||
'saved' => 'Сохранено.',
|
||||
'update_password' => 'Обновить пароль',
|
||||
'update_password_description' => 'Убедитесь, что ваша учётная запись использует длинный случайный пароль для обеспечения безопасности.',
|
||||
'current_password' => 'Текущий пароль',
|
||||
'new_password' => 'Новый пароль',
|
||||
'confirm_password' => 'Подтверждение пароля',
|
||||
'delete_account' => 'Удалить учётную запись',
|
||||
'delete_account_description' => 'После удаления вашей учётной записи все её ресурсы и данные будут удалены безвозвратно. Перед удалением учётной записи загрузите любые данные или информацию, которые вы хотите сохранить.',
|
||||
'delete_account_confirm' => 'Вы уверены, что хотите удалить свою учётную запись?',
|
||||
'delete_account_confirm_description' => 'После удаления вашей учётной записи все её ресурсы и данные будут удалены безвозвратно. Пожалуйста, введите свой пароль, чтобы подтвердить, что вы хотите навсегда удалить свою учётную запись.',
|
||||
],
|
||||
];
|
||||
148
src/lang/tr/messages.php
Normal file
148
src/lang/tr/messages.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Navigation
|
||||
'nav' => [
|
||||
'dashboard' => 'Panel',
|
||||
'knowledge_base' => 'Bilgi Tabanı',
|
||||
'profile' => 'Profil',
|
||||
'user_management' => 'Kullanıcı Yönetimi',
|
||||
'logout' => 'Çıkış',
|
||||
'login' => 'Giriş',
|
||||
'register' => 'Kayıt',
|
||||
],
|
||||
|
||||
// Documents
|
||||
'documents' => [
|
||||
'title' => 'Belgeler',
|
||||
'new_document' => 'Yeni Belge',
|
||||
'edit_document' => 'Belgeyi Düzenle',
|
||||
'edit' => 'Düzenle',
|
||||
'delete' => 'Sil',
|
||||
'save' => 'Kaydet',
|
||||
'cancel' => 'İptal',
|
||||
'created_by' => 'Oluşturan',
|
||||
'modified_by' => 'tarafından',
|
||||
'updated' => 'Güncellendi',
|
||||
'path' => 'Yol',
|
||||
'last_modified' => 'Son değişiklik',
|
||||
'no_documents' => 'Belge bulunamadı',
|
||||
'search_placeholder' => 'Belge ara...',
|
||||
'create_success' => 'Belge başarıyla oluşturuldu!',
|
||||
'update_success' => 'Belge başarıyla güncellendi!',
|
||||
'delete_success' => 'Belge başarıyla silindi!',
|
||||
'delete_confirm' => 'Bu belgeyi silmek istediğinizden emin misiniz?',
|
||||
'linked_references' => 'Bağlantılı Referanslar',
|
||||
'title_label' => 'Başlık',
|
||||
'title_placeholder' => 'Belge Başlığı (klasörler için / kullanın, örn: Laravel/Livewire/Components)',
|
||||
'title_hint' => 'İpucu: Belgeleri otomatik olarak klasörlere düzenlemek için başlıkta eğik çizgi (/) kullanın',
|
||||
'content_label' => 'İçerik',
|
||||
'content_placeholder' => 'Markdown\'ınızı buraya yazın...',
|
||||
'saving' => 'Kaydediliyor...',
|
||||
],
|
||||
|
||||
// Quick Switcher
|
||||
'quick_switcher' => [
|
||||
'title' => 'Hızlı Geçiş',
|
||||
'placeholder' => 'Belge ara...',
|
||||
'no_results' => 'Belge bulunamadı',
|
||||
'navigate' => 'gezinmek için',
|
||||
'select' => 'seçmek için',
|
||||
'close' => 'kapatmak için',
|
||||
],
|
||||
|
||||
// Admin
|
||||
'admin' => [
|
||||
'user_management' => 'Kullanıcı Yönetimi',
|
||||
'new_user' => 'Yeni Kullanıcı',
|
||||
'edit_user' => 'Kullanıcıyı Düzenle',
|
||||
'create_user' => 'Kullanıcı Oluştur',
|
||||
'users' => 'Kullanıcılar',
|
||||
'name' => 'Ad',
|
||||
'email' => 'E-posta',
|
||||
'password' => 'Şifre',
|
||||
'password_confirmation' => 'Şifre Onayı',
|
||||
'password_hint' => 'Mevcut şifreyi korumak için boş bırakın.',
|
||||
'role' => 'Rol',
|
||||
'admin' => 'Yönetici',
|
||||
'user' => 'Kullanıcı',
|
||||
'grant_admin' => 'Yönetici ayrıcalıkları ver',
|
||||
'created_at' => 'Oluşturuldu',
|
||||
'actions' => 'İşlemler',
|
||||
'edit' => 'Düzenle',
|
||||
'delete' => 'Sil',
|
||||
'no_users' => 'Kullanıcı bulunamadı.',
|
||||
'create_success' => 'Kullanıcı başarıyla oluşturuldu.',
|
||||
'update_success' => 'Kullanıcı başarıyla güncellendi.',
|
||||
'delete_success' => 'Kullanıcı başarıyla silindi.',
|
||||
'cannot_delete_self' => 'Kendinizi silemezsiniz.',
|
||||
'self_admin_warning' => 'Kendi yönetici ayrıcalıklarınızı kaldırmak yönetici paneline erişiminizi engelleyecektir.',
|
||||
],
|
||||
|
||||
// Settings
|
||||
'settings' => [
|
||||
'language' => 'Dil',
|
||||
'select_language' => 'Dil Seç',
|
||||
'language_updated' => 'Dil başarıyla güncellendi.',
|
||||
'change_language' => 'Dili değiştir',
|
||||
],
|
||||
|
||||
// Common
|
||||
'common' => [
|
||||
'save' => 'Kaydet',
|
||||
'cancel' => 'İptal',
|
||||
'delete' => 'Sil',
|
||||
'edit' => 'Düzenle',
|
||||
'create' => 'Oluştur',
|
||||
'update' => 'Güncelle',
|
||||
'back' => 'Geri',
|
||||
'confirm' => 'Onayla',
|
||||
'yes' => 'Evet',
|
||||
'no' => 'Hayır',
|
||||
'loading' => 'Yükleniyor...',
|
||||
'error' => 'Hata',
|
||||
'success' => 'Başarılı',
|
||||
],
|
||||
|
||||
// Auth
|
||||
'auth' => [
|
||||
'login' => 'Giriş',
|
||||
'register' => 'Kayıt',
|
||||
'email' => 'E-posta',
|
||||
'password' => 'Şifre',
|
||||
'remember_me' => 'Beni hatırla',
|
||||
'forgot_password' => 'Şifrenizi mi unuttunuz?',
|
||||
'confirm_password' => 'Şifre Onayı',
|
||||
'already_registered' => 'Zaten kayıtlı mısınız?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Sayfa Bulunamadı',
|
||||
'page_not_found' => 'Sayfa Bulunamadı',
|
||||
'page_not_found_description' => 'Aradığınız sayfa bulunamadı.',
|
||||
'back_to_home' => 'Ana Sayfaya Dön',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Profil',
|
||||
'information' => 'Profil Bilgileri',
|
||||
'information_description' => 'Hesabınızın profil bilgilerini ve e-posta adresini güncelleyin.',
|
||||
'name' => 'Ad',
|
||||
'email' => 'E-posta',
|
||||
'email_unverified' => 'E-posta adresiniz doğrulanmamış.',
|
||||
'resend_verification' => 'Doğrulama e-postasını yeniden göndermek için buraya tıklayın.',
|
||||
'verification_sent' => 'E-posta adresinize yeni bir doğrulama bağlantısı gönderildi.',
|
||||
'saved' => 'Kaydedildi.',
|
||||
'update_password' => 'Şifreyi Güncelle',
|
||||
'update_password_description' => 'Hesabınızın güvende kalması için uzun, rastgele bir şifre kullandığından emin olun.',
|
||||
'current_password' => 'Mevcut Şifre',
|
||||
'new_password' => 'Yeni Şifre',
|
||||
'confirm_password' => 'Şifre Onayı',
|
||||
'delete_account' => 'Hesabı Sil',
|
||||
'delete_account_description' => 'Hesabınız silindikten sonra, tüm kaynakları ve verileri kalıcı olarak silinecektir. Hesabınızı silmeden önce, saklamak istediğiniz tüm verileri veya bilgileri indirin.',
|
||||
'delete_account_confirm' => 'Hesabınızı silmek istediğinizden emin misiniz?',
|
||||
'delete_account_confirm_description' => 'Hesabınız silindikten sonra, tüm kaynakları ve verileri kalıcı olarak silinecektir. Hesabınızı kalıcı olarak silmek istediğinizi onaylamak için lütfen şifrenizi girin.',
|
||||
],
|
||||
];
|
||||
148
src/lang/uk/messages.php
Normal file
148
src/lang/uk/messages.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Navigation
|
||||
'nav' => [
|
||||
'dashboard' => 'Панель керування',
|
||||
'knowledge_base' => 'База знань',
|
||||
'profile' => 'Профіль',
|
||||
'user_management' => 'Управління користувачами',
|
||||
'logout' => 'Вийти',
|
||||
'login' => 'Увійти',
|
||||
'register' => 'Реєстрація',
|
||||
],
|
||||
|
||||
// Documents
|
||||
'documents' => [
|
||||
'title' => 'Документи',
|
||||
'new_document' => 'Новий документ',
|
||||
'edit_document' => 'Редагувати документ',
|
||||
'edit' => 'Редагувати',
|
||||
'delete' => 'Видалити',
|
||||
'save' => 'Зберегти',
|
||||
'cancel' => 'Скасувати',
|
||||
'created_by' => 'Створено',
|
||||
'modified_by' => '',
|
||||
'updated' => 'Оновлено',
|
||||
'path' => 'Шлях',
|
||||
'last_modified' => 'Остання зміна',
|
||||
'no_documents' => 'Документи не знайдено',
|
||||
'search_placeholder' => 'Пошук документів...',
|
||||
'create_success' => 'Документ успішно створено!',
|
||||
'update_success' => 'Документ успішно оновлено!',
|
||||
'delete_success' => 'Документ успішно видалено!',
|
||||
'delete_confirm' => 'Ви впевнені, що хочете видалити цей документ?',
|
||||
'linked_references' => "Пов'язані посилання",
|
||||
'title_label' => 'Назва',
|
||||
'title_placeholder' => 'Назва документа (використовуйте / для папок, наприклад Laravel/Livewire/Components)',
|
||||
'title_hint' => 'Порада: Використовуйте слеші (/) у назві для автоматичної організації документів у папки',
|
||||
'content_label' => 'Вміст',
|
||||
'content_placeholder' => 'Напишіть тут ваш markdown...',
|
||||
'saving' => 'Збереження...',
|
||||
],
|
||||
|
||||
// Quick Switcher
|
||||
'quick_switcher' => [
|
||||
'title' => 'Швидкий перехід',
|
||||
'placeholder' => 'Пошук документів...',
|
||||
'no_results' => 'Документи не знайдено',
|
||||
'navigate' => 'для навігації',
|
||||
'select' => 'для вибору',
|
||||
'close' => 'для закриття',
|
||||
],
|
||||
|
||||
// Admin
|
||||
'admin' => [
|
||||
'user_management' => 'Управління користувачами',
|
||||
'new_user' => 'Новий користувач',
|
||||
'edit_user' => 'Редагувати користувача',
|
||||
'create_user' => 'Створити користувача',
|
||||
'users' => 'Користувачі',
|
||||
'name' => "Ім'я",
|
||||
'email' => 'Email',
|
||||
'password' => 'Пароль',
|
||||
'password_confirmation' => 'Підтвердження пароля',
|
||||
'password_hint' => 'Залиште порожнім, щоб зберегти поточний пароль.',
|
||||
'role' => 'Роль',
|
||||
'admin' => 'Адміністратор',
|
||||
'user' => 'Користувач',
|
||||
'grant_admin' => 'Надати права адміністратора',
|
||||
'created_at' => 'Створено',
|
||||
'actions' => 'Дії',
|
||||
'edit' => 'Редагувати',
|
||||
'delete' => 'Видалити',
|
||||
'no_users' => 'Користувачі не знайдені.',
|
||||
'create_success' => 'Користувача успішно створено.',
|
||||
'update_success' => 'Користувача успішно оновлено.',
|
||||
'delete_success' => 'Користувача успішно видалено.',
|
||||
'cannot_delete_self' => 'Ви не можете видалити себе.',
|
||||
'self_admin_warning' => 'Зняття власних прав адміністратора заблокує вам доступ до панелі адміністратора.',
|
||||
],
|
||||
|
||||
// Settings
|
||||
'settings' => [
|
||||
'language' => 'Мова',
|
||||
'select_language' => 'Вибрати мову',
|
||||
'language_updated' => 'Мову успішно оновлено.',
|
||||
'change_language' => 'Змінити мову',
|
||||
],
|
||||
|
||||
// Common
|
||||
'common' => [
|
||||
'save' => 'Зберегти',
|
||||
'cancel' => 'Скасувати',
|
||||
'delete' => 'Видалити',
|
||||
'edit' => 'Редагувати',
|
||||
'create' => 'Створити',
|
||||
'update' => 'Оновити',
|
||||
'back' => 'Назад',
|
||||
'confirm' => 'Підтвердити',
|
||||
'yes' => 'Так',
|
||||
'no' => 'Ні',
|
||||
'loading' => 'Завантаження...',
|
||||
'error' => 'Помилка',
|
||||
'success' => 'Успіх',
|
||||
],
|
||||
|
||||
// Auth
|
||||
'auth' => [
|
||||
'login' => 'Увійти',
|
||||
'register' => 'Реєстрація',
|
||||
'email' => 'Email',
|
||||
'password' => 'Пароль',
|
||||
'remember_me' => "Запам'ятати мене",
|
||||
'forgot_password' => 'Забули пароль?',
|
||||
'confirm_password' => 'Підтвердження пароля',
|
||||
'already_registered' => 'Вже зареєстровані?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Сторінка не знайдена',
|
||||
'page_not_found' => 'Сторінка не знайдена',
|
||||
'page_not_found_description' => 'Сторінку, яку ви шукаєте, не знайдено.',
|
||||
'back_to_home' => 'Повернутися на головну',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Профіль',
|
||||
'information' => 'Інформація профілю',
|
||||
'information_description' => 'Оновіть інформацію профілю та адресу електронної пошти вашого облікового запису.',
|
||||
'name' => "Ім'я",
|
||||
'email' => 'Email',
|
||||
'email_unverified' => 'Ваша адреса електронної пошти не підтверджена.',
|
||||
'resend_verification' => 'Натисніть тут, щоб повторно відправити лист з підтвердженням.',
|
||||
'verification_sent' => 'Нове посилання для підтвердження відправлено на вашу адресу електронної пошти.',
|
||||
'saved' => 'Збережено.',
|
||||
'update_password' => 'Оновити пароль',
|
||||
'update_password_description' => 'Переконайтеся, що ваш обліковий запис використовує довгий випадковий пароль для забезпечення безпеки.',
|
||||
'current_password' => 'Поточний пароль',
|
||||
'new_password' => 'Новий пароль',
|
||||
'confirm_password' => 'Підтвердження пароля',
|
||||
'delete_account' => 'Видалити обліковий запис',
|
||||
'delete_account_description' => 'Після видалення вашого облікового запису всі його ресурси та дані будуть видалені назавжди. Перед видаленням облікового запису завантажте будь-які дані або інформацію, які ви хочете зберегти.',
|
||||
'delete_account_confirm' => 'Ви впевнені, що хочете видалити свій обліковий запис?',
|
||||
'delete_account_confirm_description' => 'Після видалення вашого облікового запису всі його ресурси та дані будуть видалені назавжди. Будь ласка, введіть свій пароль, щоб підтвердити, що ви хочете назавжди видалити свій обліковий запис.',
|
||||
],
|
||||
];
|
||||
148
src/lang/vi/messages.php
Normal file
148
src/lang/vi/messages.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Navigation
|
||||
'nav' => [
|
||||
'dashboard' => 'Bảng điều khiển',
|
||||
'knowledge_base' => 'Cơ sở tri thức',
|
||||
'profile' => 'Hồ sơ',
|
||||
'user_management' => 'Quản lý người dùng',
|
||||
'logout' => 'Đăng xuất',
|
||||
'login' => 'Đăng nhập',
|
||||
'register' => 'Đăng ký',
|
||||
],
|
||||
|
||||
// Documents
|
||||
'documents' => [
|
||||
'title' => 'Tài liệu',
|
||||
'new_document' => 'Tài liệu mới',
|
||||
'edit_document' => 'Chỉnh sửa tài liệu',
|
||||
'edit' => 'Chỉnh sửa',
|
||||
'delete' => 'Xóa',
|
||||
'save' => 'Lưu',
|
||||
'cancel' => 'Hủy',
|
||||
'created_by' => 'Được tạo bởi',
|
||||
'modified_by' => 'bởi',
|
||||
'updated' => 'Đã cập nhật',
|
||||
'path' => 'Đường dẫn',
|
||||
'last_modified' => 'Sửa đổi lần cuối',
|
||||
'no_documents' => 'Không tìm thấy tài liệu',
|
||||
'search_placeholder' => 'Tìm kiếm tài liệu...',
|
||||
'create_success' => 'Tạo tài liệu thành công!',
|
||||
'update_success' => 'Cập nhật tài liệu thành công!',
|
||||
'delete_success' => 'Xóa tài liệu thành công!',
|
||||
'delete_confirm' => 'Bạn có chắc chắn muốn xóa tài liệu này?',
|
||||
'linked_references' => 'Tham chiếu liên kết',
|
||||
'title_label' => 'Tiêu đề',
|
||||
'title_placeholder' => 'Tiêu đề tài liệu (sử dụng / cho thư mục, ví dụ: Laravel/Livewire/Components)',
|
||||
'title_hint' => 'Mẹo: Sử dụng dấu gạch chéo (/) trong tiêu đề để tự động sắp xếp tài liệu vào các thư mục',
|
||||
'content_label' => 'Nội dung',
|
||||
'content_placeholder' => 'Viết markdown của bạn ở đây...',
|
||||
'saving' => 'Đang lưu...',
|
||||
],
|
||||
|
||||
// Quick Switcher
|
||||
'quick_switcher' => [
|
||||
'title' => 'Chuyển đổi nhanh',
|
||||
'placeholder' => 'Tìm kiếm tài liệu...',
|
||||
'no_results' => 'Không tìm thấy tài liệu',
|
||||
'navigate' => 'để điều hướng',
|
||||
'select' => 'để chọn',
|
||||
'close' => 'để đóng',
|
||||
],
|
||||
|
||||
// Admin
|
||||
'admin' => [
|
||||
'user_management' => 'Quản lý người dùng',
|
||||
'new_user' => 'Người dùng mới',
|
||||
'edit_user' => 'Chỉnh sửa người dùng',
|
||||
'create_user' => 'Tạo người dùng',
|
||||
'users' => 'Người dùng',
|
||||
'name' => 'Tên',
|
||||
'email' => 'Email',
|
||||
'password' => 'Mật khẩu',
|
||||
'password_confirmation' => 'Xác nhận mật khẩu',
|
||||
'password_hint' => 'Để trống để giữ mật khẩu hiện tại.',
|
||||
'role' => 'Vai trò',
|
||||
'admin' => 'Quản trị viên',
|
||||
'user' => 'Người dùng',
|
||||
'grant_admin' => 'Cấp quyền quản trị viên',
|
||||
'created_at' => 'Đã tạo',
|
||||
'actions' => 'Hành động',
|
||||
'edit' => 'Chỉnh sửa',
|
||||
'delete' => 'Xóa',
|
||||
'no_users' => 'Không tìm thấy người dùng.',
|
||||
'create_success' => 'Tạo người dùng thành công.',
|
||||
'update_success' => 'Cập nhật người dùng thành công.',
|
||||
'delete_success' => 'Xóa người dùng thành công.',
|
||||
'cannot_delete_self' => 'Bạn không thể xóa chính mình.',
|
||||
'self_admin_warning' => 'Xóa quyền quản trị viên của chính bạn sẽ khóa quyền truy cập của bạn vào bảng quản trị.',
|
||||
],
|
||||
|
||||
// Settings
|
||||
'settings' => [
|
||||
'language' => 'Ngôn ngữ',
|
||||
'select_language' => 'Chọn ngôn ngữ',
|
||||
'language_updated' => 'Cập nhật ngôn ngữ thành công.',
|
||||
'change_language' => 'Thay đổi ngôn ngữ',
|
||||
],
|
||||
|
||||
// Common
|
||||
'common' => [
|
||||
'save' => 'Lưu',
|
||||
'cancel' => 'Hủy',
|
||||
'delete' => 'Xóa',
|
||||
'edit' => 'Chỉnh sửa',
|
||||
'create' => 'Tạo',
|
||||
'update' => 'Cập nhật',
|
||||
'back' => 'Quay lại',
|
||||
'confirm' => 'Xác nhận',
|
||||
'yes' => 'Có',
|
||||
'no' => 'Không',
|
||||
'loading' => 'Đang tải...',
|
||||
'error' => 'Lỗi',
|
||||
'success' => 'Thành công',
|
||||
],
|
||||
|
||||
// Auth
|
||||
'auth' => [
|
||||
'login' => 'Đăng nhập',
|
||||
'register' => 'Đăng ký',
|
||||
'email' => 'Email',
|
||||
'password' => 'Mật khẩu',
|
||||
'remember_me' => 'Ghi nhớ tôi',
|
||||
'forgot_password' => 'Quên mật khẩu?',
|
||||
'confirm_password' => 'Xác nhận mật khẩu',
|
||||
'already_registered' => 'Đã đăng ký?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => 'Không tìm thấy trang',
|
||||
'page_not_found' => 'Không tìm thấy trang',
|
||||
'page_not_found_description' => 'Trang bạn đang tìm kiếm không thể tìm thấy.',
|
||||
'back_to_home' => 'Quay lại trang chủ',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => 'Hồ sơ',
|
||||
'information' => 'Thông tin hồ sơ',
|
||||
'information_description' => 'Cập nhật thông tin hồ sơ và địa chỉ email của tài khoản của bạn.',
|
||||
'name' => 'Tên',
|
||||
'email' => 'Email',
|
||||
'email_unverified' => 'Địa chỉ email của bạn chưa được xác minh.',
|
||||
'resend_verification' => 'Nhấp vào đây để gửi lại email xác minh.',
|
||||
'verification_sent' => 'Một liên kết xác minh mới đã được gửi đến địa chỉ email của bạn.',
|
||||
'saved' => 'Đã lưu.',
|
||||
'update_password' => 'Cập nhật mật khẩu',
|
||||
'update_password_description' => 'Đảm bảo tài khoản của bạn đang sử dụng mật khẩu dài, ngẫu nhiên để luôn an toàn.',
|
||||
'current_password' => 'Mật khẩu hiện tại',
|
||||
'new_password' => 'Mật khẩu mới',
|
||||
'confirm_password' => 'Xác nhận mật khẩu',
|
||||
'delete_account' => 'Xóa tài khoản',
|
||||
'delete_account_description' => 'Sau khi tài khoản của bạn bị xóa, tất cả tài nguyên và dữ liệu của nó sẽ bị xóa vĩnh viễn. Trước khi xóa tài khoản của bạn, vui lòng tải xuống bất kỳ dữ liệu hoặc thông tin nào mà bạn muốn giữ lại.',
|
||||
'delete_account_confirm' => 'Bạn có chắc chắn muốn xóa tài khoản của mình?',
|
||||
'delete_account_confirm_description' => 'Sau khi tài khoản của bạn bị xóa, tất cả tài nguyên và dữ liệu của nó sẽ bị xóa vĩnh viễn. Vui lòng nhập mật khẩu của bạn để xác nhận rằng bạn muốn xóa vĩnh viễn tài khoản của mình.',
|
||||
],
|
||||
];
|
||||
@@ -115,6 +115,14 @@
|
||||
'already_registered' => '已有账号?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => '页面未找到',
|
||||
'page_not_found' => '页面未找到',
|
||||
'page_not_found_description' => '您要查找的页面不存在。',
|
||||
'back_to_home' => '返回首页',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => '个人资料',
|
||||
|
||||
@@ -115,6 +115,14 @@
|
||||
'already_registered' => '已有帳號?',
|
||||
],
|
||||
|
||||
// Errors
|
||||
'errors' => [
|
||||
'404_title' => '頁面未找到',
|
||||
'page_not_found' => '頁面未找到',
|
||||
'page_not_found_description' => '您要查找的頁面不存在。',
|
||||
'back_to_home' => '返回首頁',
|
||||
],
|
||||
|
||||
// Profile
|
||||
'profile' => [
|
||||
'title' => '個人資料',
|
||||
|
||||
31
src/resources/views/errors/404.blade.php
Normal file
31
src/resources/views/errors/404.blade.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ __('messages.errors.404_title') }} - {{ config('app.name') }}</title>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body class="font-sans antialiased">
|
||||
<div class="min-h-screen bg-gray-100 flex flex-col items-center justify-center">
|
||||
<div class="text-center">
|
||||
<h1 class="text-9xl font-bold text-gray-300">404</h1>
|
||||
<h2 class="text-2xl font-semibold text-gray-700 mt-4">
|
||||
{{ __('messages.errors.page_not_found') }}
|
||||
</h2>
|
||||
<p class="text-gray-500 mt-2">
|
||||
{{ __('messages.errors.page_not_found_description') }}
|
||||
</p>
|
||||
<div class="mt-8">
|
||||
<a href="{{ url('/') }}" class="inline-flex items-center px-6 py-3 bg-indigo-600 text-white font-medium rounded-md hover:bg-indigo-700 transition">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
{{ __('messages.errors.back_to_home') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<body class="font-sans text-gray-900 antialiased">
|
||||
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">
|
||||
<div>
|
||||
<a href="/">
|
||||
<a href="{{ url('/') }}">
|
||||
<x-application-logo class="w-20 h-20 fill-current text-gray-500" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -27,38 +27,118 @@
|
||||
@livewireStyles
|
||||
@stack('styles')
|
||||
</head>
|
||||
<body class="font-sans antialiased">
|
||||
<body class="font-sans antialiased" x-data="{
|
||||
mobileMenuOpen: false,
|
||||
sidebarWidth: localStorage.getItem('kb_sidebar_width') || 320,
|
||||
isResizing: false,
|
||||
startResize(e) {
|
||||
if (window.innerWidth < 1024) return; // lg breakpoint
|
||||
this.isResizing = true;
|
||||
document.body.style.cursor = 'col-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
},
|
||||
resize(e) {
|
||||
if (!this.isResizing) return;
|
||||
const newWidth = Math.max(200, Math.min(600, e.clientX));
|
||||
this.sidebarWidth = newWidth;
|
||||
localStorage.setItem('kb_sidebar_width', newWidth);
|
||||
},
|
||||
stopResize() {
|
||||
if (this.isResizing) {
|
||||
this.isResizing = false;
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
}
|
||||
}
|
||||
}" @mousemove.window="resize($event)" @mouseup.window="stopResize()">
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<!-- Header -->
|
||||
<header class="bg-white border-b border-gray-200 sticky top-0 z-10">
|
||||
<header class="bg-white border-b border-gray-200 sticky top-0 z-20">
|
||||
<div class="max-w-full mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex items-center space-x-3">
|
||||
<a href="/" class="flex items-center space-x-3">
|
||||
<x-application-logo class="block h-8 w-auto fill-current text-gray-800" />
|
||||
<h1 class="text-xl font-semibold text-gray-900">
|
||||
<!-- Mobile Menu Toggle -->
|
||||
<button
|
||||
@click="mobileMenuOpen = !mobileMenuOpen"
|
||||
class="lg:hidden p-2 rounded-md text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path x-show="!mobileMenuOpen" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
<path x-show="mobileMenuOpen" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<a href="{{ url('/') }}" class="flex items-center space-x-2 sm:space-x-3">
|
||||
<x-application-logo class="block h-6 sm:h-8 w-auto fill-current text-gray-800" />
|
||||
<h1 class="text-lg sm:text-xl font-semibold text-gray-900 hidden xs:block">
|
||||
{{ config('app.name', 'Knowledge Base') }}
|
||||
</h1>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="flex items-center space-x-2 sm:space-x-4">
|
||||
<!-- Quick Switcher Trigger -->
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||
class="inline-flex items-center px-2 sm:px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||
x-data
|
||||
@click.prevent="$dispatch('open-quick-switcher')"
|
||||
>
|
||||
<svg class="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="h-4 w-4 sm:mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
{{ __('messages.quick_switcher.title') }}
|
||||
<kbd class="ml-2 px-2 py-1 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded">
|
||||
<span class="hidden sm:inline">{{ __('messages.quick_switcher.title') }}</span>
|
||||
<kbd class="hidden md:inline-flex ml-2 px-2 py-1 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded">
|
||||
Ctrl+K
|
||||
</kbd>
|
||||
</button>
|
||||
|
||||
<!-- Language Switcher (for all users) -->
|
||||
<div x-data="{ open: false }" @click.away="open = false" class="relative">
|
||||
<button
|
||||
@click="open = !open"
|
||||
class="flex items-center px-2 sm:px-3 py-2 text-sm font-medium text-gray-700 hover:text-gray-900 focus:outline-none"
|
||||
title="{{ __('messages.settings.change_language') }}"
|
||||
>
|
||||
<svg class="w-4 h-4 sm:w-5 sm:h-5 sm:mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"></path>
|
||||
</svg>
|
||||
@php
|
||||
$currentLocale = app()->getLocale();
|
||||
$locales = \App\Http\Middleware\SetLocale::SUPPORTED_LOCALES;
|
||||
@endphp
|
||||
<span class="hidden sm:inline">{{ $locales[$currentLocale] ?? 'English' }}</span>
|
||||
<svg class="ml-1 h-4 w-4 hidden sm:block" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition
|
||||
class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 ring-1 ring-black ring-opacity-5 max-h-96 overflow-y-auto z-50"
|
||||
>
|
||||
@foreach($locales as $code => $name)
|
||||
<form method="POST" action="{{ route('locale.update') }}" class="inline-block w-full">
|
||||
@csrf
|
||||
<input type="hidden" name="locale" value="{{ $code }}">
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 {{ $currentLocale === $code ? 'bg-indigo-50 text-indigo-700 font-semibold' : 'text-gray-700' }}"
|
||||
>
|
||||
{{ $name }}
|
||||
@if($currentLocale === $code)
|
||||
<svg class="inline-block w-4 h-4 ml-2" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
@endif
|
||||
</button>
|
||||
</form>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@auth
|
||||
<!-- User Dropdown -->
|
||||
<div x-data="{ open: false }" @click.away="open = false" class="relative">
|
||||
@@ -66,8 +146,11 @@ class="inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-
|
||||
@click="open = !open"
|
||||
class="flex items-center text-sm font-medium text-gray-700 hover:text-gray-900 focus:outline-none"
|
||||
>
|
||||
{{ Auth::user()->name }}
|
||||
<svg class="ml-1 h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<span class="hidden md:inline">{{ Auth::user()->name }}</span>
|
||||
<span class="md:hidden w-8 h-8 bg-indigo-100 rounded-full flex items-center justify-center text-indigo-700 font-semibold">
|
||||
{{ strtoupper(substr(Auth::user()->name, 0, 1)) }}
|
||||
</span>
|
||||
<svg class="ml-1 h-4 w-4 hidden md:block" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
@@ -75,7 +158,7 @@ class="flex items-center text-sm font-medium text-gray-700 hover:text-gray-900 f
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition
|
||||
class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 ring-1 ring-black ring-opacity-5"
|
||||
class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 ring-1 ring-black ring-opacity-5 z-50"
|
||||
>
|
||||
<a href="{{ route('profile.edit') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||
{{ __('messages.nav.profile') }}
|
||||
@@ -99,9 +182,14 @@ class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 ring-1 ring
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="text-sm text-gray-700 hover:text-gray-900">
|
||||
<a href="{{ route('login') }}" class="text-sm text-gray-700 hover:text-gray-900 hidden sm:block">
|
||||
{{ __('messages.nav.login') }}
|
||||
</a>
|
||||
<a href="{{ route('login') }}" class="sm:hidden p-2 text-gray-700 hover:bg-gray-100 rounded-md" title="{{ __('messages.nav.login') }}">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"></path>
|
||||
</svg>
|
||||
</a>
|
||||
@endauth
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,8 +198,50 @@ class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 ring-1 ring
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex h-[calc(100vh-4rem)]">
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-64 bg-white border-r border-gray-200 overflow-y-auto">
|
||||
<!-- Sidebar - Desktop -->
|
||||
<aside
|
||||
id="kb-sidebar"
|
||||
class="hidden lg:block bg-white border-r border-gray-200 overflow-y-auto relative"
|
||||
:style="'width: ' + sidebarWidth + 'px'"
|
||||
>
|
||||
@livewire('sidebar-tree')
|
||||
|
||||
<!-- Resize Handle -->
|
||||
<div
|
||||
@mousedown="startResize($event)"
|
||||
class="absolute top-0 right-0 w-1 h-full cursor-col-resize hover:bg-indigo-500 transition-colors group"
|
||||
title="ドラッグして幅を変更"
|
||||
>
|
||||
<div class="absolute top-1/2 right-0 transform translate-x-1/2 -translate-y-1/2 w-1.5 h-12 bg-gray-300 rounded-full group-hover:bg-indigo-500 transition-colors"></div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Sidebar - Mobile Overlay -->
|
||||
<div
|
||||
x-show="mobileMenuOpen"
|
||||
@click="mobileMenuOpen = false"
|
||||
x-transition:enter="transition-opacity ease-linear duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition-opacity ease-linear duration-300"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="fixed inset-0 bg-gray-600 bg-opacity-75 z-30 lg:hidden"
|
||||
style="display: none;"
|
||||
></div>
|
||||
|
||||
<aside
|
||||
x-show="mobileMenuOpen"
|
||||
@click.away="mobileMenuOpen = false"
|
||||
x-transition:enter="transition ease-in-out duration-300 transform"
|
||||
x-transition:enter-start="-translate-x-full"
|
||||
x-transition:enter-end="translate-x-0"
|
||||
x-transition:leave="transition ease-in-out duration-300 transform"
|
||||
x-transition:leave-start="translate-x-0"
|
||||
x-transition:leave-end="-translate-x-full"
|
||||
class="fixed inset-y-0 left-0 top-16 w-64 bg-white border-r border-gray-200 overflow-y-auto z-40 lg:hidden"
|
||||
style="display: none;"
|
||||
>
|
||||
@livewire('sidebar-tree')
|
||||
</aside>
|
||||
|
||||
@@ -129,6 +259,91 @@ class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 ring-1 ring
|
||||
|
||||
<!-- Global Keyboard Shortcuts -->
|
||||
<script>
|
||||
// Preserve sidebar scroll position during navigation
|
||||
document.addEventListener('click', function(e) {
|
||||
const sidebar = document.getElementById('kb-sidebar');
|
||||
if (!sidebar) return;
|
||||
|
||||
const link = e.target.closest('a');
|
||||
if (link && sidebar.contains(link)) {
|
||||
const scrollPos = sidebar.scrollTop;
|
||||
sessionStorage.setItem('kb_sidebar_scroll', scrollPos);
|
||||
}
|
||||
}, true);
|
||||
|
||||
// Restore scroll position after page load
|
||||
function restoreSidebarScroll() {
|
||||
const sidebar = document.getElementById('kb-sidebar');
|
||||
if (!sidebar) return;
|
||||
|
||||
const savedPos = sessionStorage.getItem('kb_sidebar_scroll');
|
||||
if (savedPos !== null && savedPos !== '0') {
|
||||
sidebar.scrollTop = parseInt(savedPos, 10);
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight current document in sidebar
|
||||
function highlightCurrentDocument() {
|
||||
const sidebar = document.getElementById('kb-sidebar');
|
||||
if (!sidebar) {
|
||||
console.log('Sidebar not found for highlighting');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPath = window.location.pathname;
|
||||
const links = sidebar.querySelectorAll('a');
|
||||
|
||||
console.log('Current path:', currentPath);
|
||||
console.log('Found links in sidebar:', links.length);
|
||||
|
||||
links.forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
|
||||
// Remove previous highlighting
|
||||
link.classList.remove('bg-indigo-50', 'text-indigo-700', 'font-semibold');
|
||||
link.classList.add('text-gray-700');
|
||||
|
||||
const icon = link.querySelector('svg');
|
||||
if (icon) {
|
||||
icon.classList.remove('text-indigo-600');
|
||||
icon.classList.add('text-gray-400', 'group-hover:text-gray-600');
|
||||
}
|
||||
|
||||
// Check if this is the current page
|
||||
if (href === currentPath || href === window.location.href ||
|
||||
(href && currentPath && href.endsWith(currentPath))) {
|
||||
console.log('Matched link:', href, 'with current path:', currentPath);
|
||||
link.classList.add('bg-indigo-50', 'text-indigo-700', 'font-semibold');
|
||||
link.classList.remove('text-gray-700');
|
||||
|
||||
if (icon) {
|
||||
icon.classList.remove('text-gray-400', 'group-hover:text-gray-600');
|
||||
icon.classList.add('text-indigo-600');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Restore on page load
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
restoreSidebarScroll();
|
||||
highlightCurrentDocument();
|
||||
});
|
||||
} else {
|
||||
restoreSidebarScroll();
|
||||
highlightCurrentDocument();
|
||||
}
|
||||
|
||||
// Also restore on window load (for safety)
|
||||
window.addEventListener('load', () => {
|
||||
restoreSidebarScroll();
|
||||
highlightCurrentDocument();
|
||||
});
|
||||
|
||||
// Update highlight after Alpine navigation
|
||||
document.addEventListener('alpine:navigated', highlightCurrentDocument);
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
|
||||
{{ __('messages.nav.dashboard') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link href="/" :active="false">
|
||||
<x-nav-link :href="url('/')" :active="false">
|
||||
<svg class="w-4 h-4 mr-1 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"></path>
|
||||
</svg>
|
||||
@@ -82,7 +82,7 @@
|
||||
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
|
||||
{{ __('messages.nav.dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link href="/" :active="false">
|
||||
<x-responsive-nav-link :href="url('/')" :active="false">
|
||||
{{ __('messages.nav.knowledge_base') }}
|
||||
</x-responsive-nav-link>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="max-w-5xl mx-auto p-8">
|
||||
<div class="max-w-5xl mx-auto p-4 sm:p-6 lg:p-8">
|
||||
<!-- Flash Messages -->
|
||||
@if (session()->has('message'))
|
||||
<div class="mb-4 p-4 bg-green-100 border border-green-400 text-green-700 rounded">
|
||||
@@ -13,16 +13,16 @@
|
||||
@endif
|
||||
|
||||
<!-- Header -->
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<h1 class="text-3xl font-bold text-gray-900">
|
||||
<div class="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">
|
||||
{{ $isEditMode ? __('messages.documents.edit_document') : __('messages.documents.new_document') }}
|
||||
</h1>
|
||||
|
||||
<div class="flex space-x-3">
|
||||
<div class="flex flex-wrap gap-2 sm:gap-3">
|
||||
@if($isEditMode && $document)
|
||||
<a
|
||||
href="{{ route('documents.show', $document) }}"
|
||||
class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50"
|
||||
class="inline-flex items-center justify-center px-3 sm:px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 flex-1 sm:flex-none"
|
||||
>
|
||||
{{ __('messages.common.cancel') }}
|
||||
</a>
|
||||
@@ -30,14 +30,14 @@ class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md text
|
||||
<button
|
||||
wire:click="delete"
|
||||
wire:confirm="{{ __('messages.documents.delete_confirm') }}"
|
||||
class="inline-flex items-center px-4 py-2 border border-red-300 rounded-md text-sm font-medium text-red-700 bg-white hover:bg-red-50"
|
||||
class="inline-flex items-center justify-center px-3 sm:px-4 py-2 border border-red-300 rounded-md text-sm font-medium text-red-700 bg-white hover:bg-red-50 flex-1 sm:flex-none"
|
||||
>
|
||||
{{ __('messages.documents.delete') }}
|
||||
</button>
|
||||
@else
|
||||
<a
|
||||
href="{{ route('documents.show', 'home') }}"
|
||||
class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50"
|
||||
class="inline-flex items-center justify-center px-3 sm:px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 flex-1 sm:flex-none"
|
||||
>
|
||||
{{ __('messages.common.cancel') }}
|
||||
</a>
|
||||
@@ -45,12 +45,13 @@ class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md text
|
||||
|
||||
<button
|
||||
wire:click="save"
|
||||
class="inline-flex items-center px-4 py-2 bg-indigo-600 border border-transparent rounded-md text-sm font-medium text-white hover:bg-indigo-700"
|
||||
class="inline-flex items-center justify-center px-3 sm:px-4 py-2 bg-indigo-600 border border-transparent rounded-md text-sm font-medium text-white hover:bg-indigo-700 flex-1 sm:flex-none"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-4 h-4 sm:mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"></path>
|
||||
</svg>
|
||||
{{ __('messages.documents.save') }}
|
||||
<span class="hidden sm:inline">{{ __('messages.documents.save') }}</span>
|
||||
<span class="sm:hidden">{{ __('messages.documents.save') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<div class="max-w-4xl mx-auto p-8">
|
||||
<div class="max-w-4xl mx-auto p-4 sm:p-6 lg:p-8">
|
||||
<!-- Document Header -->
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h1 class="text-4xl font-bold text-gray-900">
|
||||
<div class="mb-6 sm:mb-8">
|
||||
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4 mb-4">
|
||||
<h1 class="text-2xl sm:text-3xl lg:text-4xl font-bold text-gray-900 break-words">
|
||||
{{ $document->title }}
|
||||
</h1>
|
||||
|
||||
@auth
|
||||
<a
|
||||
href="{{ route('documents.edit', $document) }}"
|
||||
class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||
class="inline-flex items-center justify-center px-3 sm:px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 whitespace-nowrap"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
|
||||
@@ -19,9 +19,9 @@ class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white text-sm font-
|
||||
@endauth
|
||||
</div>
|
||||
|
||||
<div class="flex items-center text-sm text-gray-500 space-x-4">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center text-xs sm:text-sm text-gray-500 gap-2 sm:gap-4">
|
||||
<span>
|
||||
{{ __('messages.documents.updated') }} {{ $document->updated_at->diffForHumans() }}
|
||||
{{ __('messages.documents.updated') }} {{ $document->updated_at->diffForHumans() }}({{ config('app.timezone') }})
|
||||
</span>
|
||||
|
||||
@if($document->updated_by && $document->updater)
|
||||
@@ -37,7 +37,7 @@ class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white text-sm font-
|
||||
</div>
|
||||
|
||||
<!-- Document Content -->
|
||||
<div class="prose prose-lg max-w-none mb-12">
|
||||
<div class="prose prose-sm sm:prose-base lg:prose-lg max-w-none mb-8 sm:mb-12">
|
||||
{!! $renderedContent !!}
|
||||
</div>
|
||||
|
||||
@@ -53,7 +53,6 @@ class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white text-sm font-
|
||||
<a
|
||||
href="{{ route('documents.show', $backlink) }}"
|
||||
class="block p-4 bg-gray-50 rounded-lg hover:bg-gray-100 transition"
|
||||
wire:navigate
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -70,15 +69,15 @@ class="block p-4 bg-gray-50 rounded-lg hover:bg-gray-100 transition"
|
||||
@endif
|
||||
|
||||
<!-- Document Metadata -->
|
||||
<div class="mt-12 pt-8 border-t border-gray-200">
|
||||
<div class="grid grid-cols-2 gap-4 text-sm text-gray-500">
|
||||
<div>
|
||||
<div class="mt-8 sm:mt-12 pt-6 sm:pt-8 border-t border-gray-200">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 text-xs sm:text-sm text-gray-500">
|
||||
<div class="break-all">
|
||||
<span class="font-medium">{{ __('messages.documents.path') }}:</span>
|
||||
<code class="ml-2 text-xs bg-gray-100 px-2 py-1 rounded">{{ $document->path }}</code>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">{{ __('messages.documents.last_modified') }}:</span>
|
||||
<span class="ml-2">{{ $document->updated_at->format('Y-m-d H:i:s') }}</span>
|
||||
<span class="ml-2">{{ $document->updated_at->format('Y-m-d H:i:s') }}({{ config('app.timezone') }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@ class="fixed inset-0 z-50 overflow-y-auto"
|
||||
style="display: none;"
|
||||
@click="open = false"
|
||||
>
|
||||
<div class="flex min-h-full items-start justify-center p-4 pt-[10vh]">
|
||||
<div class="flex min-h-full items-start justify-center p-2 sm:p-4 pt-[5vh] sm:pt-[10vh]">
|
||||
<div
|
||||
class="w-full max-w-2xl bg-white rounded-lg shadow-2xl"
|
||||
@click.stop
|
||||
@@ -41,16 +41,16 @@ class="w-full max-w-2xl bg-white rounded-lg shadow-2xl"
|
||||
wire:keydown.enter.prevent="selectDocument"
|
||||
>
|
||||
<!-- Search Input -->
|
||||
<div class="p-4 border-b border-gray-200">
|
||||
<div class="p-3 sm:p-4 border-b border-gray-200">
|
||||
<div class="relative">
|
||||
<svg class="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="absolute left-2 sm:left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 sm:h-5 sm:w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
<input
|
||||
x-ref="searchInput"
|
||||
type="text"
|
||||
wire:model.live="search"
|
||||
class="w-full pl-10 pr-4 py-3 border-0 focus:ring-0 text-lg"
|
||||
class="w-full pl-8 sm:pl-10 pr-4 py-2 sm:py-3 border-0 focus:ring-0 text-base sm:text-lg"
|
||||
placeholder="{{ __('messages.quick_switcher.placeholder') }}"
|
||||
autocomplete="off"
|
||||
>
|
||||
@@ -58,7 +58,7 @@ class="w-full pl-10 pr-4 py-3 border-0 focus:ring-0 text-lg"
|
||||
</div>
|
||||
|
||||
<!-- Results -->
|
||||
<div class="max-h-96 overflow-y-auto">
|
||||
<div class="max-h-60 sm:max-h-96 overflow-y-auto">
|
||||
@if(empty($this->results))
|
||||
<div class="p-8 text-center text-gray-500">
|
||||
{{ __('messages.quick_switcher.no_results') }}
|
||||
@@ -68,9 +68,8 @@ class="w-full pl-10 pr-4 py-3 border-0 focus:ring-0 text-lg"
|
||||
@foreach($this->results as $index => $result)
|
||||
<li>
|
||||
<a
|
||||
href="{{ route('documents.show', $result['id']) }}"
|
||||
href="{{ route('documents.show', $result['slug']) }}"
|
||||
class="block px-4 py-3 hover:bg-gray-50 transition {{ $index === $selectedIndex ? 'bg-indigo-50' : '' }}"
|
||||
wire:navigate
|
||||
@click="open = false"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -103,21 +102,21 @@ class="block px-4 py-3 hover:bg-gray-50 transition {{ $index === $selectedIndex
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="px-4 py-3 bg-gray-50 border-t border-gray-200 text-xs text-gray-500">
|
||||
<div class="px-3 sm:px-4 py-2 sm:py-3 bg-gray-50 border-t border-gray-200 text-xs text-gray-500">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="flex items-center space-x-2 sm:space-x-4 flex-wrap gap-y-1">
|
||||
<span class="flex items-center">
|
||||
<kbd class="px-2 py-1 bg-white border border-gray-300 rounded text-xs font-semibold mr-1">↑</kbd>
|
||||
<kbd class="px-2 py-1 bg-white border border-gray-300 rounded text-xs font-semibold mr-2">↓</kbd>
|
||||
{{ __('messages.quick_switcher.navigate') }}
|
||||
<kbd class="px-1.5 sm:px-2 py-0.5 sm:py-1 bg-white border border-gray-300 rounded text-xs font-semibold mr-1">↑</kbd>
|
||||
<kbd class="px-1.5 sm:px-2 py-0.5 sm:py-1 bg-white border border-gray-300 rounded text-xs font-semibold mr-1 sm:mr-2">↓</kbd>
|
||||
<span class="hidden sm:inline">{{ __('messages.quick_switcher.navigate') }}</span>
|
||||
</span>
|
||||
<span class="flex items-center">
|
||||
<kbd class="px-2 py-1 bg-white border border-gray-300 rounded text-xs font-semibold mr-2">↵</kbd>
|
||||
{{ __('messages.quick_switcher.select') }}
|
||||
<kbd class="px-1.5 sm:px-2 py-0.5 sm:py-1 bg-white border border-gray-300 rounded text-xs font-semibold mr-1 sm:mr-2">↵</kbd>
|
||||
<span class="hidden sm:inline">{{ __('messages.quick_switcher.select') }}</span>
|
||||
</span>
|
||||
<span class="flex items-center">
|
||||
<kbd class="px-2 py-1 bg-white border border-gray-300 rounded text-xs font-semibold mr-2">esc</kbd>
|
||||
{{ __('messages.quick_switcher.close') }}
|
||||
<kbd class="px-1.5 sm:px-2 py-0.5 sm:py-1 bg-white border border-gray-300 rounded text-xs font-semibold mr-1 sm:mr-2">esc</kbd>
|
||||
<span class="hidden sm:inline">{{ __('messages.quick_switcher.close') }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
<a
|
||||
href="{{ route('documents.show', $file['document']) }}"
|
||||
class="flex items-center px-2 py-1.5 text-sm text-gray-700 rounded hover:bg-gray-100 group"
|
||||
wire:navigate
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400 group-hover:text-gray-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
|
||||
@@ -13,12 +13,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
|
||||
<div class="max-w-xl">
|
||||
@include('profile.partials.update-locale-form')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
|
||||
<div class="max-w-xl">
|
||||
@include('profile.partials.update-password-form')
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900">
|
||||
{{ __('messages.settings.language') }}
|
||||
</h2>
|
||||
|
||||
<p class="mt-1 text-sm text-gray-600">
|
||||
{{ __('messages.settings.select_language') }}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<form method="post" action="{{ route('locale.update') }}" class="mt-6 space-y-6">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<x-input-label for="locale" :value="__('messages.settings.language')" />
|
||||
<select
|
||||
id="locale"
|
||||
name="locale"
|
||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
|
||||
>
|
||||
@foreach(\App\Http\Middleware\SetLocale::SUPPORTED_LOCALES as $code => $name)
|
||||
<option value="{{ $code }}" {{ (auth()->user()->locale ?? 'en') === $code ? 'selected' : '' }}>
|
||||
{{ $name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('locale')" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<x-primary-button>{{ __('messages.common.save') }}</x-primary-button>
|
||||
|
||||
@if (session('success'))
|
||||
<p
|
||||
x-data="{ show: true }"
|
||||
x-show="show"
|
||||
x-transition
|
||||
x-init="setTimeout(() => show = false, 2000)"
|
||||
class="text-sm text-gray-600"
|
||||
>{{ session('success') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@@ -22,11 +22,13 @@
|
||||
return view('dashboard');
|
||||
})->middleware(['auth', 'verified'])->name('dashboard');
|
||||
|
||||
// Locale switcher - available for all users (both authenticated and guest)
|
||||
Route::post('/locale', [LocaleController::class, 'update'])->name('locale.update');
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
||||
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
|
||||
Route::post('/locale', [LocaleController::class, 'update'])->name('locale.update');
|
||||
});
|
||||
|
||||
// Admin routes
|
||||
|
||||
Reference in New Issue
Block a user