php elephant sticker
Photo by RealToughCandy.com on Pexels.com

【Comprehensive Guide】Multilingual (i18n) and Accessibility in Laravel — From Language Switching to Date/Currency Localization and RTL Support

What you’ll learn (highlights first)

  • Basics of Laravel internationalization (i18n): resources/lang directory, array vs JSON translations, when to use trans / __ / trans_choice
  • Language switching design with routing/middleware (URL path / subdomain / cookie), implementation comparison and best practices
  • Accessibility essentials: HTML lang / dir, hreflang, partial lang inside pages
  • Localization of dates, times, numbers, and currency with Carbon and PHP Intl (NumberFormatter)
  • Safe support for right-to-left (RTL) languages (layout, components, icons)
  • Practical project structure for verification, testing language switching and screen reader behavior with Laravel Dusk, and documentation rules for translation consistency

Intended readers (who benefits most?)

  • Laravel beginners/intermediates: Want to add two languages (e.g., Japanese/English) quickly
  • Tech leads at agencies/SaaS: Need scalable URL design, translation workflow, QA processes
  • Designers/technical writers: Want consistent style, UI copy, alt text
  • Accessibility/QA staff: Need structured testing of language declaration, screen reading, LTR/RTL

Accessibility level: ★★★★★

Covers HTML lang and dir, partial language attributes, hreflang, accessible language switch UI, aria-live announcements, and RTL considerations (focus order, mirrored icons) with practical code.


1. Why i18n and Accessibility Together?

Multilingual support is not just about translating strings. URL/routing, screen structure, screen reader output, dates, and currencies all affect user experience. Laravel’s translation system (resources/lang), middleware, and Blade templating make it an excellent fit to implement i18n and a11y in tandem.

Key principles:

  • User choice of language always comes first; auto-detection is secondary.
  • URLs should indicate the language (better for sharing, SEO, debugging).
  • Always specify lang (and dir when needed).
  • Images, icons, ordering, and notifications also encode cultural assumptions.

This guide takes you from minimal production-ready setup to scalable practices for larger projects. ♡


2. Where and How to Store Translations: Arrays vs JSON, Pluralization, Placeholders

2.1 Directory structure

resources/
└─ lang/
   ├─ en/
   │  ├─ auth.php
   │  ├─ pagination.php
   │  ├─ validation.php
   │  └─ app.php
   ├─ ja/
   │  ├─ auth.php
   │  ├─ pagination.php
   │  ├─ validation.php
   │  └─ app.php
   ├─ en.json
   └─ ja.json
  • Array translations (e.g. app.php): use named keys like __('app.save')
  • JSON translations: use original text as the key like __('Save')"Save": "保存"
    • Great for quick start; later migrate to arrays for structure

2.2 Placeholders and pluralization

// en/app.php
return [
  'welcome_user' => 'Welcome, :name!',
  'cart_items'   => '{0} Your cart is empty|{1} You have :count item|[2,*] You have :count items',
];

// ja/app.php
return [
  'welcome_user' => ':name さん、ようこそ!',
  'cart_items'   => '{0} カートは空です|{1} :count 件の商品があります|[2,*] :count 件の商品があります',
];
<p>{{ __('app.welcome_user', ['name' => $user->name]) }}</p>
<p>{{ trans_choice('app.cart_items', $count, ['count' => $count]) }}</p>
  • Use trans_choice for plurals.
  • Always keep placeholder names consistent (name, count).

3. URL Design and Middleware: Where Do We Decide the Language?

3.1 Recommended: language code in URL path

Example: /ja/products, /en/products

  • Pros: Easy to share/bookmark, SEO-friendly, easy CDN cache separation
  • Cons: Slightly longer URLs

3.2 Example configuration

// routes/web.php
use App\Http\Middleware\SetLocaleFromUrl;

Route::middleware(SetLocaleFromUrl::class)->group(function () {
    Route::prefix('{locale}')
        ->where(['locale' => 'ja|en|ar'])
        ->group(function () {
            Route::get('/products', [ProductController::class, 'index'])->name('products.index');
        });
});
// app/Http/Middleware/SetLocaleFromUrl.php
class SetLocaleFromUrl
{
    public function handle($request, Closure $next)
    {
        $supported = ['ja','en','ar'];
        $locale = $request->route('locale');

        if (! in_array($locale, $supported)) {
            return redirect("/ja".$request->getRequestUri());
        }

        app()->setLocale($locale);
        \Carbon\Carbon::setLocale($locale);

        return $next($request);
    }
}

Alternatives

  • Subdomain (ja.example.com): useful for country-specific delivery
  • Cookie: single-domain apps (weaker for SEO/sharing)

3.3 Blade layout with lang and dir

@php
  $locale = app()->getLocale();
  $dir = in_array($locale, ['ar','he','fa']) ? 'rtl' : 'ltr';
@endphp
<html lang="{{ $locale }}" dir="{{ $dir }}">
  • Always declare lang on <html>
  • For RTL, add dir="rtl"
  • Language switch navigation should have aria-label, lang, hreflang
  • Announce current language change via aria-live

4. Translation Management Rules

  1. Split by domain: auth.php, nav.php, errors.php
  2. Keys = function + purpose: nav.settings.account
  3. Avoid reusing sentences across contexts (tone can break).
  4. Review via Pull Requests (style, screen reader output).
  5. Tests: assertSeeInOrder to verify reading order.

5. Dates, Times, Numbers, Currency

5.1 Carbon for dates/relative time

Carbon::setLocale(app()->getLocale());
$dt = Carbon::parse('2025-08-27 15:30:00');
$long  = $dt->isoFormat('LL');
$diff  = $dt->diffForHumans();
  • isoFormat = locale-appropriate display
  • diffForHumans = natural relative expressions

5.2 Intl NumberFormatter

$fmt = new \NumberFormatter(app()->getLocale(), \NumberFormatter::CURRENCY);
$price = $fmt->formatCurrency(1234.5, 'JPY');
  • Culture-specific separators, symbols, decimals
  • Always pair currency with locale

6. RTL Languages

  • Use logical CSS properties: margin-inline-start
  • Mirror arrows/icons with transform: scaleX(-1)
  • Source order must match logical focus order
  • Force direction where needed: <span dir="ltr">ABC-123</span>

7. Language Switch UI

Avoid

  • Flags (not equal to languages)
  • ISO codes alone (en, ja)

Use

  • Endonyms: 日本語 / English / العربية
  • Include lang, hreflang
  • aria-live announcement of current language

8. Validation Messages

Translate field names in validation.php:

'attributes' => ['email' => 'メールアドレス']
  • Avoid double maintenance between attributes() and lang files
  • Use role="alert" and aria-invalid="true" for accessible error output

9. SEO: hreflang

Specify alternate language URLs in <head>:

<link rel="alternate" hreflang="ja" href="/ja/...">
<link rel="alternate" hreflang="en" href="/en/...">
<link rel="alternate" hreflang="x-default" href="/en/...">

Avoid forced redirects; suggest language on first visit only.


10. Minimal Example App

Example controllers, views, and translation files (see original doc).


11–16. Additional Topics

  • Alt text, captions, transcripts for media
  • Localized emails, PDFs, error pages
  • Accessibility checklist (lang, dir, translations, SEO)
  • Feature tests and Dusk E2E for switching & screen reader output
  • Common pitfalls (e.g., mixing currency with wrong locale)
  • Team workflows: treat dictionary + UI copy as product assets

17. Conclusion

  • Use URLs + middleware for explicit control
  • Array/JSON translations, trans_choice for plurals
  • Carbon/Intl for localized dates, numbers, currency
  • Accessible UI: language switch with aria-live, RTL-safe layouts
  • Test & review processes to make dictionary and copy part of your product asset base

This is the foundation of “inclusive multilingual experiences”. Proper i18n × a11y lowers both language and context barriers, strengthening trust and attachment to your product. Start with these examples, and grow your Laravel app into one that truly speaks to everyone. ♡


Who benefits most

  • SaaS/EC PMs: Standardize URL & dictionary ops for smooth cross-team workflows
  • Tech leads: Incremental i18n rollout, QA templates for RTL/currency
  • Designers/writers: Consistent UI copy, alt text, captions
  • Accessibility/QA: Structured checklists for lang/dir/hreflang, RTL, live announcements

By greeden

Leave a Reply

Your email address will not be published. Required fields are marked *

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)