php 52 lines · 8 steps

Serving cached translations from a Laravel controller

A single-action controller assembles locale strings and serves them with ETag-based HTTP caching.

Explained by highlit
1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Http\JsonResponse;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\File;
8use Illuminate\Support\Str;
9 
10class TranslationController extends Controller
11{
12 protected array $groups = ['auth', 'validation', 'pagination', 'messages'];
13 
14 public function __invoke(Request $request, string $locale): JsonResponse
15 {
16 abort_unless(in_array($locale, config('app.available_locales', ['en'])), 404);
17 
18 app()->setLocale($locale);
19 
20 $translations = collect($this->groups)
21 ->mapWithKeys(fn (string $group) => [$group => trans($group)])
22 ->merge($this->jsonStrings($locale))
23 ->toArray();
24 
25 $etag = md5(json_encode($translations));
26 
27 if ($request->header('If-None-Match') === $etag) {
28 return response()->json(null, 304);
29 }
30 
31 return response()
32 ->json([
33 'locale' => $locale,
34 'strings' => $translations,
35 ])
36 ->setEtag($etag)
37 ->header('Cache-Control', 'public, max-age=3600');
38 }
39 
40 protected function jsonStrings(string $locale): array
41 {
42 $path = lang_path("{$locale}.json");
43 
44 if (! File::exists($path)) {
45 return [];
46 }
47 
48 return collect(json_decode(File::get($path), true))
49 ->mapWithKeys(fn ($value, $key) => [Str::slug($key, '_') => $value])
50 ->toArray();
51 }
52}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1ETags let clients skip re-downloading unchanged payloads by returning a cheap 304.
  2. 2Validating input against an allow-list before acting keeps a public endpoint safe.
  3. 3Collections chain transformations cleanly, merging multiple sources into one array.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Serving cached translations from a Laravel controller — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code