add locale manager

This commit is contained in:
2024-03-27 13:56:52 +05:00
parent 7edca39546
commit 7443ff19d6
23 changed files with 845 additions and 497 deletions

View File

@@ -4,6 +4,12 @@ APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
NOVA_PATH="/work-place"
LOCALE_APP_API_TOKEN=""
LOCALE_APP_URL=http://127.0.0.1:8001
LOCALE_APP_PATH=
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

View File

@@ -18,6 +18,23 @@ function isLocalIp(string $ip = ''): bool
return ! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
}
/**
* Is turkmen ip
* @param string $ip
*/
function isTurkmenIp(string $ip = ''): bool
{
$patterns = ['95.85', '216'];
foreach ($patterns as $pattern) {
if (strpos($ip, $pattern) === 0) {
return true;
}
}
return false;
}
/**
* Un mask phone from "+(993)-xx-xx-xx-xx"
*/
@@ -138,3 +155,19 @@ function storeResourceEvent(string $name, array $data, Request $request): void
Log::error('Error in storeResourceEvent() helpers', ['error' => $e]);
}
}
/**
* Locale app path
*/
function localeAppPath(): string
{
return config('app.locale_app.path');
}
/**
* Locale app url
*/
function localeAppUrl(): string
{
return config('app.locale_app.url');
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
class LocaleManagerController extends Controller
{
//
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Models\System\Locale;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class LocaleManager extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
];
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Nova\Actions;
use App\Repos\System\Locale\LocaleManagerRepo;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Actions\ActionResponse;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
class ExportTranslations extends Action
{
use InteractsWithQueue, Queueable;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models): mixed
{
LocaleManagerRepo::make()->handle();
return ActionResponse::message('It worked!');
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request): array
{
return [];
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace App\Nova\Resources\System\Locale;
use App\Models\System\Locale\LocaleManager;
use App\Nova\Actions\ExportTranslations;
use App\Nova\Resource;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;
class LocaleManagerResource extends Resource
{
/**
* The model the resource corresponds to.
*
* @var class-string<\App\Models\System\Locale\LocaleManager>
*/
public static $model = LocaleManager::class;
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'name';
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'name',
];
/**
* Get the fields displayed by the resource.
*/
public function fields(NovaRequest $request): array
{
return [
ID::make()->sortable(),
Text::make(__('Main'), 'name'),
];
}
/**
* Get the cards available for the request.
*
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* @return array
*/
public function filters(NovaRequest $request)
{
return [];
}
/**
* Get the lenses available for the resource.
*
* @return array
*/
public function lenses(NovaRequest $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* @return array
*/
public function actions(NovaRequest $request): array
{
return [
ExportTranslations::make()
->standalone(),
];
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace App\Policies\System\Locale;
use App\Models\System\Locale\LocaleManager;
use App\Models\User;
class LocaleManagerPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
if ($user->isMe()) {
return true;
}
return false;
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, LocaleManager $localeManager): bool
{
if ($user->isMe()) {
return true;
}
return false;
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
if ($user->isMe()) {
return true;
}
return false;
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, LocaleManager $localeManager): bool
{
if ($user->isMe()) {
return true;
}
return false;
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, LocaleManager $localeManager): bool
{
if ($user->isMe()) {
return true;
}
return false;
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, LocaleManager $localeManager): bool
{
if ($user->isMe()) {
return true;
}
return false;
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, LocaleManager $localeManager): bool
{
if ($user->isMe()) {
return true;
}
return false;
}
}

View File

@@ -7,14 +7,17 @@ use App\Models\Order\Card\CardState;
use App\Models\Order\Card\CardType;
use App\Models\Order\Loan\LoanOrder;
use App\Models\Order\Loan\LoanType;
use App\Models\System\Locale\LocaleManager;
use App\Models\System\Location\Province;
use App\Models\System\Roles\Permission;
use App\Models\System\Roles\Role;
use App\Models\User;
use App\Policies\Branch\BranchPolicy;
use App\Policies\Order\Card\CardStatePolicy;
use App\Policies\Order\Card\CardTypePolicy;
use App\Policies\Order\Loan\LoanOrderPolicy;
use App\Policies\Order\Loan\LoanTypePolicy;
use App\Policies\System\Locale\LocaleManagerPolicy;
use App\Policies\System\Location\ProvincePolicy;
use App\Policies\System\Logs\ActionEventPolicy;
use App\Policies\System\Roles\PermissionPolicy;
@@ -53,6 +56,9 @@ class AuthServiceProvider extends ServiceProvider
Branch::class => BranchPolicy::class,
Province::class => ProvincePolicy::class,
// Locale manager...
LocaleManager::class => LocaleManagerPolicy::class,
// ActionsEvents...
ActionEvent::class => ActionEventPolicy::class,
];

View File

@@ -0,0 +1,101 @@
<?php
namespace App\Repos\System\Locale;
use Exception;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;
use Symfony\Component\Filesystem\Filesystem;
class LocaleManagerRepo
{
/**
* Locale app path
*/
protected string $localeAppPath;
/**
* Locale app url
*/
protected string $localeAppUrl;
/**
* Locale app api token
*/
protected string $localeAppApiToken;
/**
* Files system
*/
protected Filesystem $fileSystem;
/**
* Locale manager app
*/
public function __construct()
{
$this->localeAppPath = config('app.locale_app.path');
$this->localeAppUrl = config('app.locale_app.url');
$this->localeAppApiToken = config('app.locale_app.api_token');
$this->fileSystem = new Filesystem();
}
/**
* Magic way of new
*/
public static function make(): self
{
return new self();
}
/**
* Run locale manager
*/
public function handle(): void
{
$this->exportTranslations()
->syncTranslationsWithLocaleApp();
}
/**
* Locale app translations directory
*/
public function localeAppTranslationsDirectory(): string
{
return $this->localeAppPath . DIRECTORY_SEPARATOR . 'lang';
}
/**
* Export translations to translate app
*/
public function exportTranslations(): self
{
$this->fileSystem->mirror(lang_path(), $this->localeAppTranslationsDirectory());
return $this;
}
/**
* Sync translations with locale app
*/
public function syncTranslationsWithLocaleApp(): self
{
$response = Http::acceptJson()->withHeaders([
'Api-Token' => $this->localeAppApiToken,
])
->retry(
times: 3,
sleepMilliseconds: 50,
throw: false,
when: function (Exception $exception, PendingRequest $request) {
return true;
})
->post(
url: $this->localeAppUrl . '/api/import-translations',
data: []
);
return $this;
}
}

View File

@@ -12,6 +12,7 @@ use App\Nova\Resources\Order\Card\Requisite\CardRequisite;
use App\Nova\Resources\Order\Loan\LoanOrder;
use App\Nova\Resources\Order\Loan\LoanPaidOffLetterOrder;
use App\Nova\Resources\Order\Loan\LoanType;
use App\Nova\Resources\System\Locale\LocaleManagerResource;
use App\Nova\Resources\System\Location\Province;
use App\Nova\Resources\System\Roles\Permission;
use App\Nova\Resources\System\Roles\Role;
@@ -64,6 +65,11 @@ class NovaMenuRepo
MenuItem::resource(Province::class),
MenuItem::resource(Branch::class),
])->collapsedByDefault(),
MenuGroup::make(__('Locale'), [
MenuItem::resource(LocaleManagerResource::class),
])->collapsedByDefault(),
])->icon('cog')->collapsedByDefault(),
MenuSection::make(__('Backups'))

View File

@@ -28,6 +28,7 @@
"spatie/nova-backup-tool": "^5.0",
"stepanenko3/nova-logs-tool": "^2.1",
"stevebauman/location": "^7.1",
"symfony/filesystem": "^7.0",
"trin4ik/nova-switcher": "^0.4.0"
},
"require-dev": {
@@ -38,7 +39,6 @@
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^7.0",
"phpunit/phpunit": "^10.1",
"singlequote/laravel-locale-finder": "^1.0",
"spatie/laravel-ignition": "^2.0"
},
"autoload": {

727
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -91,6 +91,12 @@ return [
'en' => 'English',
],
'locale_app' => [
'path' => env('LOCALE_APP_PATH', ''),
'url' => env('LOCALE_APP_URL', ''),
'api_token' => env('LOCALE_APP_API_TOKEN', ''),
],
/*
|--------------------------------------------------------------------------
| Application Fallback Locale

View File

@@ -1,77 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Determine the paths
|--------------------------------------------------------------------------
|
*/
'paths' => [
'lang_folder' => base_path('lang'),
],
/*
|--------------------------------------------------------------------------
| The search options
|--------------------------------------------------------------------------
|
*/
'search' => [
/*
|--------------------------------------------------------------------------
| The folders the package uses to scan for translation keys
|--------------------------------------------------------------------------
|
*/
'folders' => [
base_path('app'),
resource_path('views'),
],
/*
|--------------------------------------------------------------------------
| Enter specific files
|--------------------------------------------------------------------------
|
*/
'files' => [
],
/*
|--------------------------------------------------------------------------
| The folders the package excludes from scanning
|--------------------------------------------------------------------------
|
*/
'exclude' => [
'storage',
],
/*
|--------------------------------------------------------------------------
| The files the package uses to scan for translation keys
|--------------------------------------------------------------------------
|
*/
'file_extension' => [
'*.php',
'*.js',
],
],
/*
|--------------------------------------------------------------------------
| The methods used to translate keys
|--------------------------------------------------------------------------
|
*/
'translation_methods' => [
'\$t',
'i18n.t',
'@lang',
'__',
'trans_choice',
],
];

74
config/translations.php Normal file
View File

@@ -0,0 +1,74 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Source Language
|--------------------------------------------------------------------------
|
| This is the language that will be used as the source language for
| the translations. This language will be used to import the
| translations from the files.
|
*/
'source_language' => env('TRANSLATIONS_SOURCE_LANGUAGE', 'en'),
/*
|--------------------------------------------------------------------------
| Exclude Files
|--------------------------------------------------------------------------
|
| The following files will be ignored during the import process.
| and those files will be ignored in every language.
|
*/
'exclude_files' => [
//'validation.php', // Exclude default validation for example.
],
/*
|--------------------------------------------------------------------------
| Laravel Translations Path
|--------------------------------------------------------------------------
|
| The default is `translations` but you can change it to whatever works best and
| doesn't conflict with the routing in your application.
|
*/
'path' => env('TRANSLATIONS_PATH', 'translations'),
/*
|--------------------------------------------------------------------------
| Laravel Translations Custom Domain
|--------------------------------------------------------------------------
| You may change the domain where Laravel Translations should be active.
| If the domain is empty, all domains will be valid.
|
*/
'domain' => env('TRANSLATIONS_DOMAIN', null),
/*
|--------------------------------------------------------------------------
| Laravel Translations route middleware
|--------------------------------------------------------------------------
|
| These middleware will be assigned to every Laravel Translations route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web'],
/*
|--------------------------------------------------------------------------
| Database Connection
|--------------------------------------------------------------------------
|
| The database connection that should be used to store the imported
| translations You may specify the connection as a string
| which is the name of the connection in the database.php file
|
*/
'database_connection' => env('TRANSLATIONS_DB_CONNECTION', null),
];

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('locale_managers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('locale_managers');
}
};

1
lang/en.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -8,6 +8,7 @@
"Already Reported": "Уже сообщалось",
"Bad Gateway": "Плохой шлюз",
"Bad Request": "Некорректный запрос",
"Locale": "Языковой стандарт",
"Bandwidth Limit Exceeded": "Исчерпана пропускная ширина канала",
"Before proceeding, please check your email for a verification link.": "Прежде чем продолжить, проверьте свою электронную почту на наличие ссылки для подтверждения.",
"click here to request another": "нажмите здесь для запроса другой ссылки",

View File

@@ -6,6 +6,7 @@
"Accepted": "Kabul edildi",
"Address": "Salgysy",
"Ahal": "Ahal",
"Locale": "Dil",
"All rights reserved.": "Rightshli hukuklar goralandyr.",
"Already Reported": "Eýýäm habar berildi",
"Arkadag": "Arkadag",

1
public/vendor/translations/app.css vendored Normal file

File diff suppressed because one or more lines are too long

1
public/vendor/translations/app.js vendored Normal file
View File

@@ -0,0 +1 @@
(()=>{var r,e={80:()=>{},662:()=>{}},o={};function n(r){var t=o[r];if(void 0!==t)return t.exports;var a=o[r]={exports:{}};return e[r](a,a.exports,n),a.exports}n.m=e,r=[],n.O=(e,o,t,a)=>{if(!o){var v=1/0;for(p=0;p<r.length;p++){for(var[o,t,a]=r[p],i=!0,f=0;f<o.length;f++)(!1&a||v>=a)&&Object.keys(n.O).every((r=>n.O[r](o[f])))?o.splice(f--,1):(i=!1,a<v&&(v=a));if(i){r.splice(p--,1);var l=t();void 0!==l&&(e=l)}}return e}a=a||0;for(var p=r.length;p>0&&r[p-1][2]>a;p--)r[p]=r[p-1];r[p]=[o,t,a]},n.o=(r,e)=>Object.prototype.hasOwnProperty.call(r,e),(()=>{var r={260:0,143:0};n.O.j=e=>0===r[e];var e=(e,o)=>{var t,a,[v,i,f]=o,l=0;if(v.some((e=>0!==r[e]))){for(t in i)n.o(i,t)&&(n.m[t]=i[t]);if(f)var p=f(n)}for(e&&e(o);l<v.length;l++)a=v[l],n.o(r,a)&&r[a]&&r[a][0](),r[a]=0;return n.O(p)},o=self.webpackChunk=self.webpackChunk||[];o.forEach(e.bind(null,0)),o.push=e.bind(null,o.push.bind(o))})(),n.O(void 0,[143],(()=>n(80)));var t=n.O(void 0,[143],(()=>n(662)));t=n.O(t)})();

View File

@@ -0,0 +1,4 @@
{
"/app.js": "/app.js",
"/app.css": "/app.css"
}

View File

@@ -54,8 +54,15 @@
</div>
</div>
</div>
@if(isTurkmenIp(request()->ip()))
<script src="/assets/js/inputmask.min.js"></script>
<script src="/assets/js/sweetalert2.js"></script>
@else
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/inputmask@latest/dist/inputmask.min.js"></script>
@endif
<script src="/assets/js/cookieconsent.js"></script>
<script src="/assets/js/fn.js"></script>
<script src="/assets/js/app.js"></script>