Files
nnterp-react-admin/app/Http/Controllers/TranslationController.php
2026-03-13 20:49:46 +08:00

420 lines
18 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Setting;
use App\Models\AddOn;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Cookie;
use Inertia\Inertia;
use Illuminate\Support\Facades\Artisan;
class TranslationController extends Controller
{
private function getAllowedLanguages(): array
{
$languagesData = json_decode(File::get(resource_path('lang/language.json')), true);
return collect($languagesData)->pluck('code')->toArray();
}
public function getTranslations($locale)
{
$locale = strtolower($locale);
if (!in_array($locale, array_map('strtolower', $this->getAllowedLanguages()))) {
$locale = 'en';
}
$path = resource_path("lang/{$locale}.json");
if (!File::exists($path)) {
$path = resource_path("lang/en.json");
$locale = 'en';
}
// $layoutDirection = in_array($locale, ['ar', 'he']) ? 'rtl' : 'ltr';
$layoutDirection = in_array($locale, ['ar', 'he']) ? 'rtl' : 'ltr';
$translations = json_decode(File::get($path), true) ?? [];
// Merge enabled package translations
$enabledPackages = AddOn::where('is_enable', true)->pluck('module');
foreach ($enabledPackages as $packageName) {
$packageLangFile = base_path("packages/workdo/{$packageName}/src/Resources/lang/{$locale}.json");
if (File::exists($packageLangFile)) {
$packageTranslations = json_decode(File::get($packageLangFile), true) ?? [];
$translations = array_merge($translations, $packageTranslations);
}
}
if (empty($translations)) {
return response()->json(['error' => __('Invalid translation file')], 500);
}
app()->setLocale($locale);
return response()->json([
'translations' => $translations,
'layoutDirection' => null,
'locale' => $locale
]);
}
public function manage(Request $request)
{
if(auth()->user()->type == 'superadmin') {
$currentLanguage = $request->get('lang', 'en');
$search = $request->get('search', '');
$page = $request->get('page', 1);
$perPage = 50;
if (!in_array($currentLanguage, $this->getAllowedLanguages())) {
$currentLanguage = 'en';
}
// Load current language translations
$path = resource_path("lang/{$currentLanguage}.json");
$allTranslations = [];
if (File::exists($path)) {
$allTranslations = json_decode(File::get($path), true) ?? [];
}
// Filter translations based on search
$filteredTranslations = $allTranslations;
if ($search) {
$filteredTranslations = array_filter($allTranslations, function($value, $key) use ($search) {
return stripos($key, $search) !== false || stripos($value, $search) !== false;
}, ARRAY_FILTER_USE_BOTH);
}
// Paginate translations
$total = count($filteredTranslations);
$lastPage = $total > 0 ? ceil($total / $perPage) : 1;
$offset = ($page - 1) * $perPage;
$paginatedTranslations = array_slice($filteredTranslations, $offset, $perPage, true);
$paginationData = [
'current_page' => (int)$page,
'data' => $paginatedTranslations,
'first_page_url' => $request->url() . '?' . http_build_query(array_merge($request->except('page'), ['page' => 1])),
'from' => $total > 0 ? $offset + 1 : 0,
'last_page' => $lastPage,
'last_page_url' => $request->url() . '?' . http_build_query(array_merge($request->except('page'), ['page' => $lastPage])),
'next_page_url' => $page < $lastPage ? $request->url() . '?' . http_build_query(array_merge($request->except('page'), ['page' => $page + 1])) : null,
'path' => $request->url(),
'per_page' => $perPage,
'prev_page_url' => $page > 1 ? $request->url() . '?' . http_build_query(array_merge($request->except('page'), ['page' => $page - 1])) : null,
'to' => min($offset + $perPage, $total),
'total' => $total
];
// Get enabled packages list only
$enabledPackages = AddOn::where('is_enable', true)->get(['package_name', 'name']);
// Get available languages with flags
$languagesData = json_decode(File::get(resource_path('lang/language.json')), true);
$availableLanguages = collect($languagesData)
->filter(fn($lang) => ($lang['enabled'] ?? true))
->map(function ($lang) {
return [
'code' => $lang['code'],
'name' => $lang['name'],
'countryCode' => $lang['countryCode'],
'flag' => $this->getCountryFlag($lang['countryCode'])
];
})->values()->toArray();
// Get current language status
$currentLangData = collect($languagesData)->firstWhere('code', $currentLanguage);
$isCurrentLanguageEnabled = $currentLangData['enabled'] ?? true;
return Inertia::render('languages/manage', [
'currentLanguage' => $currentLanguage,
'translations' => $paginationData,
'enabledPackages' => $enabledPackages,
'availableLanguages' => $availableLanguages,
'isCurrentLanguageEnabled' => $isCurrentLanguageEnabled,
'filters' => ['search' => $search]
]);
}
else
{
return redirect()->route('dashboard')->with('error', __('Permission denied'));
}
}
public function updateTranslations(Request $request, $locale)
{
if (!in_array($locale, $this->getAllowedLanguages())) {
return response()->json(['error' => __('Invalid language')], 400);
}
$request->validate([
'translations' => 'required|array'
], [
'translations.required' => __('Translations are required.'),
'translations.array' => __('Translations must be a valid array.'),
]);
$translations = $request->input('translations');
$path = resource_path("lang/{$locale}.json");
try {
// Save new translations
$currentTranslations = json_decode(File::get($path), true) ?? [];
$translations = array_merge($currentTranslations, $translations);
File::put($path, json_encode($translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
return response()->json(['success' => true, 'message' => __('The translation details are updated successfully.')]);
} catch (\Exception $e) {
return response()->json(['error' => __('Failed to save translations: :error', ['error' => $e->getMessage()])], 500);
}
}
public function getPackageTranslations(Request $request, $locale, $packageName)
{
$search = $request->get('search', '');
$page = $request->get('page', 1);
$perPage = 50;
if (!in_array($locale, $this->getAllowedLanguages())) {
return response()->json(['error' => __('Invalid language')], 400);
}
$package = AddOn::where('package_name', $packageName)->where('is_enable', true)->first();
if (!$package) {
return response()->json(['error' => __('Package not found or disabled')], 404);
}
$packageLangFile = base_path("packages/workdo/{$package->module}/src/Resources/lang/{$locale}.json");
if (!File::exists($packageLangFile)) {
$packageLangFile = base_path("packages/workdo/{$package->module}/src/Resources/lang/en.json");
if (!File::exists($packageLangFile)) {
return response()->json(['translations' => []]);
}
}
$translations = json_decode(File::get($packageLangFile), true) ?? [];
// Filter translations based on search
$filteredTranslations = $translations;
if ($search) {
$filteredTranslations = array_filter($translations, function($value, $key) use ($search) {
return stripos($key, $search) !== false || stripos($value, $search) !== false;
}, ARRAY_FILTER_USE_BOTH);
}
// Paginate translations
$total = count($filteredTranslations);
$lastPage = $total > 0 ? ceil($total / $perPage) : 1;
$offset = ($page - 1) * $perPage;
$paginatedTranslations = array_slice($filteredTranslations, $offset, $perPage, true);
$paginationData = [
'current_page' => (int)$page,
'data' => $paginatedTranslations,
'first_page_url' => $request->url() . '?' . http_build_query(array_merge($request->except('page'), ['page' => 1])),
'from' => $total > 0 ? $offset + 1 : 0,
'last_page' => $lastPage,
'last_page_url' => $request->url() . '?' . http_build_query(array_merge($request->except('page'), ['page' => $lastPage])),
'next_page_url' => $page < $lastPage ? $request->url() . '?' . http_build_query(array_merge($request->except('page'), ['page' => $page + 1])) : null,
'path' => $request->url(),
'per_page' => $perPage,
'prev_page_url' => $page > 1 ? $request->url() . '?' . http_build_query(array_merge($request->except('page'), ['page' => $page - 1])) : null,
'to' => min($offset + $perPage, $total),
'total' => $total
];
return response()->json(['translations' => $paginationData]);
}
public function updatePackageTranslations(Request $request, $locale, $packageName)
{
if (!in_array($locale, $this->getAllowedLanguages())) {
return response()->json(['error' => __('Invalid language')], 400);
}
$package = AddOn::where('package_name', $packageName)->where('is_enable', true)->first();
if (!$package) {
return response()->json(['error' => __('Package not found or disabled')], 404);
}
$request->validate([
'translations' => 'required|array'
], [
'translations.required' => __('Package translations are required.'),
'translations.array' => __('Package translations must be a valid array.'),
]);
$translations = $request->input('translations');
$packageLangFile = base_path("packages/workdo/{$package->module}/src/Resources/lang/{$locale}.json");
try {
$packageLangDir = dirname($packageLangFile);
if (!File::exists($packageLangDir)) {
File::makeDirectory($packageLangDir, 0755, true);
}
if (File::exists($packageLangFile)) {
$currentTranslations = json_decode(File::get($packageLangFile), true) ?? [];
$translations = array_merge($currentTranslations, $translations);
}
File::put($packageLangFile, json_encode($translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
return response()->json(['success' => true, 'message' => __('The package translations updated successfully.')]);
} catch (\Exception $e) {
return response()->json(['error' => __('Failed to save package translations: :error', ['error' => $e->getMessage()])], 500);
}
}
public function createLanguage(Request $request)
{
$request->validate([
'code' => 'required|string|max:10',
'name' => 'required|string|max:255',
'countryCode' => 'required|string|size:2'
], [
'code.required' => __('Language code is required.'),
'code.string' => __('Language code must be a valid string.'),
'code.max' => __('Language code must not exceed 10 characters.'),
'name.required' => __('Language name is required.'),
'name.string' => __('Language name must be a valid string.'),
'name.max' => __('Language name must not exceed 255 characters.'),
'countryCode.required' => __('Country code is required.'),
'countryCode.string' => __('Country code must be a valid string.'),
'countryCode.size' => __('Country code must be exactly 2 characters.'),
]);
try {
// Check if language already exists in language.json
$languagesFile = resource_path('lang/language.json');
$languages = json_decode(File::get($languagesFile), true);
$existingLanguage = collect($languages)->firstWhere('code', $request->code);
if ($existingLanguage) {
return response()->json(['error' => __('The language code already exists')], 422);
}
$languages[] = [
'code' => $request->code,
'name' => $request->name,
'countryCode' => strtoupper($request->countryCode)
];
File::put($languagesFile, json_encode($languages, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
// Copy en.json to new language
$enFile = resource_path('lang/en.json');
$newLangFile = resource_path("lang/{$request->code}.json");
if (File::exists($enFile)) {
File::copy($enFile, $newLangFile);
}
// Copy package translations
$enabledPackages = AddOn::where('is_enable', true)->pluck('module');
foreach ($enabledPackages as $packageName) {
$packageEnFile = base_path("packages/workdo/{$packageName}/src/Resources/lang/en.json");
$packageNewFile = base_path("packages/workdo/{$packageName}/src/Resources/lang/{$request->code}.json");
if (File::exists($packageEnFile)) {
$packageDir = dirname($packageNewFile);
if (!File::exists($packageDir)) {
File::makeDirectory($packageDir, 0755, true);
}
File::copy($packageEnFile, $packageNewFile);
}
}
return response()->json(['success' => true, 'message' => __('The language has been created successfully.')]);
} catch (\Exception $e) {
return response()->json(['error' => __('Failed to create language: :error', ['error' => $e->getMessage()])], 500);
}
}
public function deleteLanguage($languageCode)
{
if ($languageCode === 'en') {
return response()->json(['error' => __('Cannot delete English language')], 422);
}
try {
// Remove from language.json
$languagesFile = resource_path('lang/language.json');
$languages = json_decode(File::get($languagesFile), true);
$languages = array_filter($languages, fn($lang) => $lang['code'] !== $languageCode);
File::put($languagesFile, json_encode(array_values($languages), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
// Delete main language file
$mainLangFile = resource_path("lang/{$languageCode}.json");
if (File::exists($mainLangFile)) {
File::delete($mainLangFile);
}
// Delete package language files
$enabledPackages = AddOn::where('is_enable', true)->pluck('module');
foreach ($enabledPackages as $packageName) {
$packageLangFile = base_path("packages/workdo/{$packageName}/src/Resources/lang/{$languageCode}.json");
if (File::exists($packageLangFile)) {
File::delete($packageLangFile);
}
}
return response()->json(['success' => true, 'message' => __('The language has been deleted.')]);
} catch (\Exception $e) {
return response()->json(['error' => __('Failed to delete language: :error', ['error' => $e->getMessage()])], 500);
}
}
public function toggleLanguageStatus($languageCode)
{
if ($languageCode === 'en') {
return response()->json(['error' => __('Cannot disable English language')], 422);
}
try {
$languagesFile = resource_path('lang/language.json');
$languages = json_decode(File::get($languagesFile), true);
foreach ($languages as &$language) {
if ($language['code'] === $languageCode) {
$language['enabled'] = !($language['enabled'] ?? true);
break;
}
}
File::put($languagesFile, json_encode($languages, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
return response()->json(['success' => true, 'message' => __('The language status updated successfully.')]);
} catch (\Exception $e) {
return response()->json(['error' => __('Failed to update language status: :error', ['error' => $e->getMessage()])], 500);
}
}
public function changeLanguage(Request $request)
{
if (!auth()->check()) {
return response()->json(['error' => __('Unauthorized')], 401);
}
$request->validate([
'lang' => 'required|string'
]);
$locale = strtolower($request->input('lang'));
// $layoutDirection = in_array($locale, ['ar', 'he']) ? 'rtl' : 'ltr';
auth()->user()->update(['lang' => $locale]);
// when company's user change then update company setting
// setSetting('layoutDirection', $layoutDirection);
}
private function getCountryFlag(string $countryCode): string
{
if (strlen($countryCode) !== 2) {
return '🌐'; // Default flag for invalid codes
}
$codePoints = str_split(strtoupper($countryCode));
$codePoints = array_map(fn($char) => 127397 + ord($char), $codePoints);
return mb_convert_encoding('&#' . implode(';&#', $codePoints) . ';', 'UTF-8', 'HTML-ENTITIES');
}
}