|
10 | 10 |
|
11 | 11 | class GetLocaleController extends Controller
|
12 | 12 | {
|
| 13 | + private const LOCALE_NOT_FOUND = 'Locale not found.'; |
| 14 | + |
13 | 15 | /**
|
| 16 | + * Handle the incoming request. |
| 17 | + * |
14 | 18 | * @throws \Throwable
|
15 | 19 | */
|
16 | 20 | public function __invoke(string $locale): JsonResponse
|
17 | 21 | {
|
18 |
| - // Doesn't match our whitelist |
19 |
| - abort_unless(in_array($locale, config('locale-via-api.locales'), true), 404); |
20 |
| - |
21 |
| - // Might be on whitelist, but doesn't exist |
22 |
| - abort_unless(File::exists(lang_path($locale)), 404); |
| 22 | + // Ensure locale is valid and exists |
| 23 | + $this->ensureLocaleIsValid($locale); |
| 24 | + $this->ensureLocaleExists($locale); |
23 | 25 |
|
24 |
| - $data = Cache::driver(config('locale-via-api.cache.driver', 'sync'))->remember( |
25 |
| - config('locale-via-api.cache.prefix', 'locale-via-api:') . $locale, |
| 26 | + // Get cached data or generate it if not present |
| 27 | + $data = Cache::driver(config('locale-via-api.cache.driver', 'array'))->remember( |
| 28 | + $this->getCacheKey($locale), |
26 | 29 | config('locale-via-api.cache.duration', 3600),
|
27 | 30 | function () use ($locale) {
|
28 |
| - return $this->getLocaleData($locale); |
29 |
| - }); |
| 31 | + return $this->getMergedLocaleData($locale); |
| 32 | + } |
| 33 | + ); |
30 | 34 |
|
31 |
| - return response()->json([ |
32 |
| - 'data' => $data, |
33 |
| - 'meta' => [ |
34 |
| - 'hash' => md5(json_encode($data)), |
35 |
| - ], |
36 |
| - ]); |
| 35 | + // Return JSON response |
| 36 | + return $this->createJsonResponse($data); |
37 | 37 | }
|
38 | 38 |
|
| 39 | + /** |
| 40 | + * Ensure the locale is valid. |
| 41 | + * |
| 42 | + * @throws \Symfony\Component\HttpKernel\Exception\HttpException |
| 43 | + */ |
| 44 | + private function ensureLocaleIsValid(string $locale): void |
| 45 | + { |
| 46 | + abort_unless(in_array($locale, config('locale-via-api.locales'), true), 404, self::LOCALE_NOT_FOUND); |
| 47 | + } |
| 48 | + |
| 49 | + /** |
| 50 | + * Ensure the locale directory exists. |
| 51 | + * |
| 52 | + * @throws \Symfony\Component\HttpKernel\Exception\HttpException |
| 53 | + */ |
| 54 | + private function ensureLocaleExists(string $locale): void |
| 55 | + { |
| 56 | + abort_unless(File::exists(lang_path($locale)), 404, self::LOCALE_NOT_FOUND); |
| 57 | + } |
| 58 | + |
| 59 | + /** |
| 60 | + * Get the cache key for the given locale. |
| 61 | + */ |
| 62 | + private function getCacheKey(string $locale): string |
| 63 | + { |
| 64 | + return sprintf('%s%s', config('locale-via-api.cache.prefix', 'locale-via-api:'), $locale); |
| 65 | + } |
| 66 | + |
| 67 | + /** |
| 68 | + * Get merged locale data. |
| 69 | + */ |
| 70 | + private function getMergedLocaleData(string $locale): array |
| 71 | + { |
| 72 | + $data = $this->getLocaleData($locale); |
| 73 | + |
| 74 | + if (config('locale-via-api.load_vendor_files', true)) { |
| 75 | + // Get vendor directories |
| 76 | + $vendorLocales = File::directories(lang_path('vendor')); |
| 77 | + |
| 78 | + foreach ($vendorLocales as $vendorLocale) { |
| 79 | + $vendorName = basename($vendorLocale); |
| 80 | + $data = array_merge_recursive( |
| 81 | + $data, |
| 82 | + $this->getVendorLocaleData(sprintf('vendor/%s/%s', $vendorName, $locale), $vendorName) |
| 83 | + ); |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + ksort($data); |
| 88 | + |
| 89 | + return $data; |
| 90 | + } |
| 91 | + |
| 92 | + /** |
| 93 | + * Get locale data from files. |
| 94 | + */ |
39 | 95 | protected function getLocaleData(string $locale): array
|
| 96 | + { |
| 97 | + return $this->loadLocaleFiles(lang_path($locale)); |
| 98 | + } |
| 99 | + |
| 100 | + /** |
| 101 | + * Get vendor locale data from files. |
| 102 | + */ |
| 103 | + protected function getVendorLocaleData(string $path, string $vendorName): array |
| 104 | + { |
| 105 | + return $this->loadLocaleFiles(lang_path($path), sprintf('vendor.%s', $vendorName)); |
| 106 | + } |
| 107 | + |
| 108 | + /** |
| 109 | + * Load locale files from a given path. |
| 110 | + */ |
| 111 | + protected function loadLocaleFiles(string $directory, string $prefix = ''): array |
40 | 112 | {
|
41 | 113 | $data = [];
|
42 |
| - $files = File::allFiles(lang_path($locale)); |
43 | 114 |
|
44 |
| - foreach ($files as $file) { |
45 |
| - $fileName = Str::before($file->getFilename(), '.'); |
| 115 | + if (! File::exists($directory)) { |
| 116 | + return $data; |
| 117 | + } |
| 118 | + |
| 119 | + $files = File::allFiles($directory); |
46 | 120 |
|
47 |
| - // No support for JSON files right now |
| 121 | + foreach ($files as $file) { |
48 | 122 | if ($file->getExtension() !== 'php') {
|
49 | 123 | continue;
|
50 | 124 | }
|
51 | 125 |
|
52 |
| - // This is a directory |
53 |
| - if (! Str::is($locale, $file->getRelativePath())) { |
54 |
| - $fileName = Str::replace('/', '.', Str::before($file->getRelativePathname(), '.')); |
55 |
| - $fileName = Str::replace($locale . '.', '', $fileName); |
| 126 | + $relativePath = Str::replaceFirst($directory . DIRECTORY_SEPARATOR, '', $file->getPathname()); |
| 127 | + $fileName = Str::before($relativePath, '.'); |
| 128 | + |
| 129 | + // Convert the relative path to a dot notation key |
| 130 | + $key = Str::replace(DIRECTORY_SEPARATOR, '.', $fileName); |
| 131 | + |
| 132 | + if ($prefix) { |
| 133 | + $key = sprintf('%s.%s', $prefix, $key); |
56 | 134 | }
|
57 | 135 |
|
58 |
| - $data[$fileName] = File::getRequire($file); |
| 136 | + $data[$key] = File::getRequire($file); |
59 | 137 | }
|
60 | 138 |
|
61 | 139 | return $data;
|
62 | 140 | }
|
| 141 | + |
| 142 | + /** |
| 143 | + * Create a JSON response. |
| 144 | + */ |
| 145 | + private function createJsonResponse(array $data): JsonResponse |
| 146 | + { |
| 147 | + return response()->json([ |
| 148 | + 'data' => $data, |
| 149 | + 'meta' => [ |
| 150 | + 'hash' => md5(json_encode($data)), |
| 151 | + ], |
| 152 | + ]); |
| 153 | + } |
63 | 154 | }
|
0 commit comments