Пошаговое руководство по локализации Laravel

Выведите свой бизнес на новый уровень благодаря глобальной экспансии. Рост и развитие бизнеса.
Оглавление

Laravel — это мощный инструмент, позволяющий разработчикам создавать многоязычные веб-сайты. Внедрение веб-локализации в ваши Laravel позволяет обеспечить персонализированный пользовательский опыт для посетителей с разным языковым опытом, что в конечном итоге расширяет охват аудитории и повышает вовлеченность пользователей.

Мы шаг за шагом расскажем вам о внедрении локализации в ваше Laravel приложение и познакомим вас с инструментом, который может упростить и улучшить ваши усилия по локализации!

Почему следует локализовать веб-сайт Laravel ?

Мужчина в оранжевом жилете держит планшет с картой мира. Показаны флаги различных стран.

Вот несколько важных причин, почему вам следует локализовать свой веб-сайт Laravel .

  • Охватите глобальную аудиторию: локализация вашего Laravel позволит расширить охват вашего приложения для международной аудитории. Это даст возможность пользователям из разных стран и с разным языковым опытом понимать ваш контент и взаимодействовать с ним.
  • Улучшает пользовательский опыт: локализация позволяет пользователям взаимодействовать с приложением на своем родном языке, что значительно улучшает пользовательский опыт. Это может повысить вовлеченность пользователей, снизить показатель отказов и увеличить конверсию.
  • Конкурентное преимущество: на конкурентном глобальном рынке предложение приложений на нескольких языках может дать значительное преимущество перед конкурентами. Это демонстрирует вашу приверженность международным рынкам и может помочь вам более эффективно выходить на новые рынки.
  • Улучшает SEO: Хорошо локализованные веб-сайты, как правило, занимают более высокие позиции в поисковых системах по запросам на определенном языке. Это может увеличить посещаемость вашего блога и онлайн-видимость на различных рынках благодаря многоязычному переводу.

Требования к многоязычной локализации Laravel

Два человека работают вместе за большим компьютерным экраном, используя различные приложения. Похоже, они трудятся сообща.

При реализации многоязычной локализации в Laravelнеобходимо учитывать несколько требований и шагов.

  • Чтобы получить доступ к новейшим функциям локализации, убедитесь, что вы используете последнюю версию Laravel (например, версию 10.x).
  • Базовое понимание PHP и фреймворка Laravel будет полезно в процессе внедрения.
  • Настройте локальную среду разработки или сервер, поддерживающий Laravel, включая веб-сервер и базу данных.
  • С самого начала определите, какие языки будет поддерживать ваше приложение.

Простые переводы Laravel

Мужчина работает за ноутбуком. На экране — веб-браузер.

После ознакомления с требованиями, которые необходимо выполнить перед переводом приложения или веб-сайта Laravel , мы предложим несколько простых шагов для перевода Laravel .

Для этого откройте файл представления, который вы хотите локализовать, например, resources/views/welcome.blade.php. Затем замените тег <body> следующим кодом.

				
					<body class="antialiased">
    <div class="relative flex items-top justify-center min-h-screen bg-gray-100 sm:items-center py-4 sm:pt-0">
        <div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
            <div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
                Welcome to Linguise website!
            </div>
        </div>
    </div> <script data-no-optimize="1">window.lazyLoadOptions=Object.assign({},{threshold:300},window.lazyLoadOptions||{});!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).LazyLoad=e()}(this,function(){"use strict";function e(){return(e=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n,a=arguments[e];for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(t[n]=a[n])}return t}).apply(this,arguments)}function o(t){return e({},at,t)}function l(t,e){return t.getAttribute(gt+e)}function c(t){return l(t,vt)}function s(t,e){return function(t,e,n){e=gt+e;null!==n?t.setAttribute(e,n):t.removeAttribute(e)}(t,vt,e)}function i(t){return s(t,null),0}function r(t){return null===c(t)}function u(t){return c(t)===_t}function d(t,e,n,a){t&&(void 0===a?void 0===n?t(e):t(e,n):t(e,n,a))}function f(t,e){et?t.classList.add(e):t.className+=(t.className?" ":"")+e}function _(t,e){et?t.classList.remove(e):t.className=t.className.replace(new RegExp("(^|\\s+)"+e+"(\\s+|$)")," ").replace(/^\s+/,"").replace(/\s+$/,"")}function g(t){return t.llTempImage}function v(t,e){!e||(e=e._observer)&&e.unobserve(t)}function b(t,e){t&&(t.loadingCount+=e)}function p(t,e){t&&(t.toLoadCount=e)}function n(t){for(var e,n=[],a=0;e=t.children[a];a+=1)"SOURCE"===e.tagName&&n.push(e);return n}function h(t,e){(t=t.parentNode)&&"PICTURE"===t.tagName&&n(t).forEach(e)}function a(t,e){n(t).forEach(e)}function m(t){return!!t[lt]}function E(t){return t[lt]}function I(t){return delete t[lt]}function y(e,t){var n;m(e)||(n={},t.forEach(function(t){n[t]=e.getAttribute(t)}),e[lt]=n)}function L(a,t){var o;m(a)&&(o=E(a),t.forEach(function(t){var e,n;e=a,(t=o[n=t])?e.setAttribute(n,t):e.removeAttribute(n)}))}function k(t,e,n){f(t,e.class_loading),s(t,st),n&&(b(n,1),d(e.callback_loading,t,n))}function A(t,e,n){n&&t.setAttribute(e,n)}function O(t,e){A(t,rt,l(t,e.data_sizes)),A(t,it,l(t,e.data_srcset)),A(t,ot,l(t,e.data_src))}function w(t,e,n){var a=l(t,e.data_bg_multi),o=l(t,e.data_bg_multi_hidpi);(a=nt&&o?o:a)&&(t.style.backgroundImage=a,n=n,f(t=t,(e=e).class_applied),s(t,dt),n&&(e.unobserve_completed&&v(t,e),d(e.callback_applied,t,n)))}function x(t,e){!e||0<e.loadingCount||0<e.toLoadCount||d(t.callback_finish,e)}function M(t,e,n){t.addEventListener(e,n),t.llEvLisnrs[e]=n}function N(t){return!!t.llEvLisnrs}function z(t){if(N(t)){var e,n,a=t.llEvLisnrs;for(e in a){var o=a[e];n=e,o=o,t.removeEventListener(n,o)}delete t.llEvLisnrs}}function C(t,e,n){var a;delete t.llTempImage,b(n,-1),(a=n)&&--a.toLoadCount,_(t,e.class_loading),e.unobserve_completed&&v(t,n)}function R(i,r,c){var l=g(i)||i;N(l)||function(t,e,n){N(t)||(t.llEvLisnrs={});var a="VIDEO"===t.tagName?"loadeddata":"load";M(t,a,e),M(t,"error",n)}(l,function(t){var e,n,a,o;n=r,a=c,o=u(e=i),C(e,n,a),f(e,n.class_loaded),s(e,ut),d(n.callback_loaded,e,a),o||x(n,a),z(l)},function(t){var e,n,a,o;n=r,a=c,o=u(e=i),C(e,n,a),f(e,n.class_error),s(e,ft),d(n.callback_error,e,a),o||x(n,a),z(l)})}function T(t,e,n){var a,o,i,r,c;t.llTempImage=document.createElement("IMG"),R(t,e,n),m(c=t)||(c[lt]={backgroundImage:c.style.backgroundImage}),i=n,r=l(a=t,(o=e).data_bg),c=l(a,o.data_bg_hidpi),(r=nt&&c?c:r)&&(a.style.backgroundImage='url("'.concat(r,'")'),g(a).setAttribute(ot,r),k(a,o,i)),w(t,e,n)}function G(t,e,n){var a;R(t,e,n),a=e,e=n,(t=Et[(n=t).tagName])&&(t(n,a),k(n,a,e))}function D(t,e,n){var a;a=t,(-1<It.indexOf(a.tagName)?G:T)(t,e,n)}function S(t,e,n){var a;t.setAttribute("loading","lazy"),R(t,e,n),a=e,(e=Et[(n=t).tagName])&&e(n,a),s(t,_t)}function V(t){t.removeAttribute(ot),t.removeAttribute(it),t.removeAttribute(rt)}function j(t){h(t,function(t){L(t,mt)}),L(t,mt)}function F(t){var e;(e=yt[t.tagName])?e(t):m(e=t)&&(t=E(e),e.style.backgroundImage=t.backgroundImage)}function P(t,e){var n;F(t),n=e,r(e=t)||u(e)||(_(e,n.class_entered),_(e,n.class_exited),_(e,n.class_applied),_(e,n.class_loading),_(e,n.class_loaded),_(e,n.class_error)),i(t),I(t)}function U(t,e,n,a){var o;n.cancel_on_exit&&(c(t)!==st||"IMG"===t.tagName&&(z(t),h(o=t,function(t){V(t)}),V(o),j(t),_(t,n.class_loading),b(a,-1),i(t),d(n.callback_cancel,t,e,a)))}function $(t,e,n,a){var o,i,r=(i=t,0<=bt.indexOf(c(i)));s(t,"entered"),f(t,n.class_entered),_(t,n.class_exited),o=t,i=a,n.unobserve_entered&&v(o,i),d(n.callback_enter,t,e,a),r||D(t,n,a)}function q(t){return t.use_native&&"loading"in HTMLImageElement.prototype}function H(t,o,i){t.forEach(function(t){return(a=t).isIntersecting||0<a.intersectionRatio?$(t.target,t,o,i):(e=t.target,n=t,a=o,t=i,void(r(e)||(f(e,a.class_exited),U(e,n,a,t),d(a.callback_exit,e,n,t))));var e,n,a})}function B(e,n){var t;tt&&!q(e)&&(n._observer=new IntersectionObserver(function(t){H(t,e,n)},{root:(t=e).container===document?null:t.container,rootMargin:t.thresholds||t.threshold+"px"}))}function J(t){return Array.prototype.slice.call(t)}function K(t){return t.container.querySelectorAll(t.elements_selector)}function Q(t){return c(t)===ft}function W(t,e){return e=t||K(e),J(e).filter(r)}function X(e,t){var n;(n=K(e),J(n).filter(Q)).forEach(function(t){_(t,e.class_error),i(t)}),t.update()}function t(t,e){var n,a,t=o(t);this._settings=t,this.loadingCount=0,B(t,this),n=t,a=this,Y&&window.addEventListener("online",function(){X(n,a)}),this.update(e)}var Y="undefined"!=typeof window,Z=Y&&!("onscroll"in window)||"undefined"!=typeof navigator&&/(gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent),tt=Y&&"IntersectionObserver"in window,et=Y&&"classList"in document.createElement("p"),nt=Y&&1<window.devicePixelRatio,at={elements_selector:".lazy",container:Z||Y?document:null,threshold:300,thresholds:null,data_src:"src",data_srcset:"srcset",data_sizes:"sizes",data_bg:"bg",data_bg_hidpi:"bg-hidpi",data_bg_multi:"bg-multi",data_bg_multi_hidpi:"bg-multi-hidpi",data_poster:"poster",class_applied:"applied",class_loading:"litespeed-loading",class_loaded:"litespeed-loaded",class_error:"error",class_entered:"entered",class_exited:"exited",unobserve_completed:!0,unobserve_entered:!1,cancel_on_exit:!0,callback_enter:null,callback_exit:null,callback_applied:null,callback_loading:null,callback_loaded:null,callback_error:null,callback_finish:null,callback_cancel:null,use_native:!1},ot="src",it="srcset",rt="sizes",ct="poster",lt="llOriginalAttrs",st="loading",ut="loaded",dt="applied",ft="error",_t="native",gt="data-",vt="ll-status",bt=[st,ut,dt,ft],pt=[ot],ht=[ot,ct],mt=[ot,it,rt],Et={IMG:function(t,e){h(t,function(t){y(t,mt),O(t,e)}),y(t,mt),O(t,e)},IFRAME:function(t,e){y(t,pt),A(t,ot,l(t,e.data_src))},VIDEO:function(t,e){a(t,function(t){y(t,pt),A(t,ot,l(t,e.data_src))}),y(t,ht),A(t,ct,l(t,e.data_poster)),A(t,ot,l(t,e.data_src)),t.load()}},It=["IMG","IFRAME","VIDEO"],yt={IMG:j,IFRAME:function(t){L(t,pt)},VIDEO:function(t){a(t,function(t){L(t,pt)}),L(t,ht),t.load()}},Lt=["IMG","IFRAME","VIDEO"];return t.prototype={update:function(t){var e,n,a,o=this._settings,i=W(t,o);{if(p(this,i.length),!Z&&tt)return q(o)?(e=o,n=this,i.forEach(function(t){-1!==Lt.indexOf(t.tagName)&&S(t,e,n)}),void p(n,0)):(t=this._observer,o=i,t.disconnect(),a=t,void o.forEach(function(t){a.observe(t)}));this.loadAll(i)}},destroy:function(){this._observer&&this._observer.disconnect(),K(this._settings).forEach(function(t){I(t)}),delete this._observer,delete this._settings,delete this.loadingCount,delete this.toLoadCount},loadAll:function(t){var e=this,n=this._settings;W(t,n).forEach(function(t){v(t,e),D(t,n,e)})},restoreAll:function(){var e=this._settings;K(e).forEach(function(t){P(t,e)})}},t.load=function(t,e){e=o(e);D(t,e)},t.resetStatus=function(t){i(t)},t}),function(t,e){"use strict";function n(){e.body.classList.add("litespeed_lazyloaded")}function a(){console.log("[LiteSpeed] Start Lazy Load"),o=new LazyLoad(Object.assign({},t.lazyLoadOptions||{},{elements_selector:"[data-lazyloaded]",callback_finish:n})),i=function(){o.update()},t.MutationObserver&&new MutationObserver(i).observe(e.documentElement,{childList:!0,subtree:!0,attributes:!0})}var o,i;t.addEventListener?t.addEventListener("load",a,!1):t.attachEvent("onload",a)}(window,document);</script><script data-no-optimize="1">window.litespeed_ui_events=window.litespeed_ui_events||["mouseover","click","keydown","wheel","touchmove","touchstart"];var urlCreator=window.URL||window.webkitURL;function litespeed_load_delayed_js_force(){console.log("[LiteSpeed] Start Load JS Delayed"),litespeed_ui_events.forEach(e=>{window.removeEventListener(e,litespeed_load_delayed_js_force,{passive:!0})}),document.querySelectorAll("iframe[data-litespeed-src]").forEach(e=>{e.setAttribute("src",e.getAttribute("data-litespeed-src"))}),"loading"==document.readyState?window.addEventListener("DOMContentLoaded",litespeed_load_delayed_js):litespeed_load_delayed_js()}litespeed_ui_events.forEach(e=>{window.addEventListener(e,litespeed_load_delayed_js_force,{passive:!0})});async function litespeed_load_delayed_js(){let t=[];for(var d in document.querySelectorAll('script[type="litespeed/javascript"]').forEach(e=>{t.push(e)}),t)await new Promise(e=>litespeed_load_one(t[d],e));document.dispatchEvent(new Event("DOMContentLiteSpeedLoaded")),window.dispatchEvent(new Event("DOMContentLiteSpeedLoaded"))}function litespeed_load_one(t,e){console.log("[LiteSpeed] Load ",t);var d=document.createElement("script");d.addEventListener("load",e),d.addEventListener("error",e),t.getAttributeNames().forEach(e=>{"type"!=e&&d.setAttribute("data-src"==e?"src":e,t.getAttribute(e))});let a=!(d.type="text/javascript");!d.src&&t.textContent&&(d.src=litespeed_inline2src(t.textContent),a=!0),t.after(d),t.remove(),a&&e()}function litespeed_inline2src(t){try{var d=urlCreator.createObjectURL(new Blob([t.replace(/^(?:<!--)?(.*?)(?:-->)?$/gm,"$1")],{type:"text/javascript"}))}catch(e){d="data:text/javascript;base64,"+btoa(t.replace(/^(?:<!--)?(.*?)(?:-->)?$/gm,"$1"))}return d}</script><script data-no-optimize="1">var litespeed_vary=document.cookie.replace(/(?:(?:^|.*;\s*)_lscache_vary\s*\=\s*([^;]*).*$)|^.*$/,"");litespeed_vary||fetch("/wp-content/plugins/litespeed-cache/guest.vary.php",{method:"POST",cache:"no-cache",redirect:"follow"}).then(e=>e.json()).then(e=>{console.log(e),e.hasOwnProperty("reload")&&"yes"==e.reload&&(sessionStorage.setItem("litespeed_docref",document.referrer),window.location.reload(!0))});</script><script data-optimized="1" type="litespeed/javascript" data-src="https://www.linguise.com/wp-content/litespeed/js/dcfb0d8aef9b57b394f38c36a44631fb.js?ver=7271c"></script></body>
				
			

Как видите, приведенный выше текст в настоящее время написан непосредственно в коде. Это менее эффективно и затрудняет перевод веб-сайтов на разные языки (интернационализацию).

Мы собираемся сделать приведенный выше текст более гибким, чтобы его можно было легко адаптировать к разным языкам. Laravel предоставляет для этого очень полезную функцию — систему локализации. В качестве первого шага замените существующий текст следующим кодом.

				
					{{ __('Welcome to Linguise website!') }}
				
			

Laravel по умолчанию отобразит приведенный выше текст и выполнит поиск перевода, если пользователь выберет язык, отличный от английского. В этом случае английский будет использоваться в качестве языка приложения по умолчанию.

Настройка языковых настроек в многоязычном веб-приложении Laravel

Два человека работают за компьютерными экранами. Они взаимодействуют с большими дисплеями.

Но как Laravel определяет текущий язык или узнает, какие языки доступны в приложении? Он проверяет настройки локали в файле config/app.php. Откройте этот файл и найдите следующие два ключа.

				
					/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/

'locale' => 'en',

/*
|--------------------------------------------------------------------------
| Application Fallback Locale
|--------------------------------------------------------------------------
|
| The fallback locale determines the locale to use when the current one
| is not available. You may change the value to correspond to any of
| the language folders that are provided through your application.
|
*/

'fallback_locale' => 'en',

				
			

Приведенное выше пояснение к ключам должно быть понятным. Вкратце, ключ locale содержит локаль по умолчанию для вашего приложения (если в коде не указана другая локаль). Параметр fallback_locale активируется, если в вашем приложении запрашивается несуществующая локаль.

Теперь добавим в этот файл новый ключ, который будет содержать список всех поддерживаемых языковых версий.

				
					/*
|--------------------------------------------------------------------------
| Available locales
|--------------------------------------------------------------------------
|
| List all locales that your application works with
|
*/

'available_locales' => [
  'English' => 'en',
  'Italian' => 'it',
  'French' => 'fr',
],


				
			

На данный момент мы протестировали веб-версию Laravel с поддержкой трех языков: английского, итальянского и французского.

Обзор файлов перевода Laravel

Женщина указывает на большой экран компьютера, на котором отображаются различные красочные значки. Экран имеет белый фон с синими и фиолетовыми значками.

В Laravel, как и во многих других фреймворках, переводы для разных языков хранятся в отдельных файлах. Для организации этих файлов переводов используются два метода.

Более старый метод хранит файлы в следующей структуре: resources/lang/{en,fr,it}/{myfile.php}. Более новый метод использует JSON-файлы, например, resources/lang/{fr.json, it.json}. В этой статье основное внимание будет уделено более новому методу, хотя принципы работы схожи и со старым, за исключением различий в именовании и доступе к ключам перевода.

Для языков с региональными различиями следует называть языковые каталоги или файлы в соответствии со стандартом ISO 15897. Например, британский английский следует называть en_GB вместо en-gb.

Общая информация

В Laravel, как и во многих фреймворках, переводы для разных языков хранятся в отдельных файлах. Существует два основных метода организации файлов переводов Laravel .

  1. Традиционный подход предполагает хранение файлов по следующему пути: resources/lang/{en,fr,it}/{myfile.php}.
  2. Современный подход использует файлы resources/lang/{fr.json, it.json}.

В этой статье основное внимание будет уделено второму методу, хотя принципы применимы к обоим (с различиями в именовании и доступе к ключам перевода).

Для языков, различающихся в зависимости от региона, рекомендуется называть языковые каталоги/файлы в соответствии со стандартами ISO 15897. Например, британский английский будет обозначаться как en_GB, а не en-gb.

Создание файлов перевода Laravel

Настроив языковые параметры для нашего приложения, мы можем приступить к переводу нашего стандартного приветственного сообщения.

Начнём с создания новых файлов локализации в формате JSON в каталоге resources/lang. Сначала создадим файл resources/lang/it.json и заполним его соответствующими переводами.

				
					{
  "Welcome to Linguise website!": "Benvenuti nel sito web di Linguise!"
}

				
			

Далее добавьте файл resources/lang/fr.json.

				
					{
 "Welcome to Linguise website!": "Bienvenue sur le site de Linguise"
}

				
			

Как вы можете заметить, мы постоянно ссылаемся на сообщение по умолчанию из файла welcome.blade.php ({{ __('Добро пожаловать на сайт Linguise !') }}). Нет необходимости создавать файл en.json, поскольку Laravel автоматически распознает, что сообщения по умолчанию на английском языке.

Настройка переключения языка в многоязычном приложении Laravel

Люди взаимодействуют с большим интерфейсом электронной почты.

Кроме того, Laravel пока не настроен на переопределение локального языка, поэтому на данный момент мы будем обрабатывать перевод непосредственно в маршруте. Измените маршрут приветствия по умолчанию в файле routes/web.php следующим образом.

				
					Route::get('/{locale?}', function ($locale = null) {
    if (isset($locale) && in_array($locale, config('app.available_locales'))) {
        app()->setLocale($locale);
    }
    
    return view('welcome');
});
				
			

В данном случае мы получаем необязательный GET-параметр локали и устанавливаем текущую локаль на его основе (если запрошенная локаль поддерживается).

Теперь вы можете посетить свой веб-сайт и добавить любой из поддерживаемых языков в качестве первого сегмента URL. Например, при переходе по адресу localhost/it или localhost/fr отобразится локализованный контент. Если вы не укажете язык или выберете неподдерживаемый, Laravel по умолчанию будет использовать английский (en).

Промежуточное ПО для локализации Laravel

Включение языкового параметра в каждый URL-адрес может быть неоптимальным решением и нарушить визуальную привлекательность сайта. Для решения этой проблемы мы настроим переключатель языков и будем использовать пользовательскую сессию для отображения переведенного контента. Вы можете создать новое промежуточное ПО в файле app/Http/Middleware/Localization.php или сгенерировать его, выполнив команду artisan make:middleware Localization.

Затем добавьте следующий код внутрь.

				
					<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Session;

class Localization
{
    /**
    * Handle an incoming request.
    *
    * @param  \Illuminate\Http\Request  $request
    * @param  \Closure  $next
    * @return mixed
    */
    public function handle(Request $request, Closure $next)
    {
        if (Session::has('locale')) {
            App::setLocale(Session::get('locale'));
        }
        return $next($request);
    }
}


				
			

Это промежуточное ПО укажет Laravel использовать местоположение, выбранное пользователем, если такая опция присутствует в сессии.

Поскольку нам необходимо, чтобы эта операция выполнялась при каждом запросе, добавьте её в стек промежуточного ПО по умолчанию в файле app/Http/Kernel.php для группы промежуточного ПО для веб-приложений.

				
					/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
  'web' => [
    \App\Http\Middleware\EncryptCookies::class,
    \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
    \Illuminate\Session\Middleware\StartSession::class,
    \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    \App\Http\Middleware\VerifyCsrfToken::class,
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
    \App\Http\Middleware\Localization::class, // <--- add this
],


				
			

Изменение маршрутов

Далее, в файле routes/web.php определите маршрут для изменения локали. Здесь мы используем маршрут через замыкание, но при желании вы можете разместить тот же код внутри контроллера.

				
					Route::get('language/{locale}', function ($locale) {
   app()->setLocale($locale);
   session()->put('locale', $locale);
   return redirect()->back();
});

				
			

Кроме того, удалите переключение локали, которое ранее было добавлено к маршруту приветствия по умолчанию. Теперь ваш корневой маршрут должен выглядеть так.

				
					Route::get('/', function () {
   return view('welcome');
});

				
			

После этого пользователь сможет переключить активный язык, только перейдя по адресу localhost/language/{locale}. Выбранный язык будет сохранен в сессии, и пользователи будут перенаправлены обратно на предыдущую страницу (это обрабатывается промежуточным ПО локализации). 

Для проверки перейдите по адресу localhost/language/it (при условии, что в вашем браузере активен сессионный cookie), и вы должны увидеть переведенный контент. Вы можете перемещаться по сайту или обновлять страницу, и выбранный язык останется в силе.

Реализация переключения языка

Теперь нам нужно предоставить пользователю возможность переключать язык в веб-интерфейсе Laravel , вместо того, чтобы заставлять его вручную вводить коды локали в URL-адрес. Для этого создайте простой переключатель языка. Добавьте новый файл по адресу resources/views/partials/language_switcher.blade.php и вставьте следующий код.

				
					<div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
    @foreach($available_locales as $locale_name => $available_locale)
        @if($available_locale === $current_locale)
            <span class="ml-2 mr-2 text-gray-700">{{ $locale_name }}</span>
        @else
            <a class="ml-1 underline ml-2 mr-2" href="language/{{ $available_locale }}">
                <span>{{ $locale_name }}</span>
            </a>
        @endif
    @endforeach
</div>


				
			

Чтобы добавить недавно созданный переключатель языков в окно «Добро пожаловать», просто добавьте следующую строку в файл welcome.blade.php в том месте, где вы хотите, чтобы переключатель отображался.

				
					<body class="antialiased">
    <div class="relative flex items-top justify-center min-h-screen bg-gray-100 sm:items-center py-4 sm:pt-0">
        <div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
            @include('partials/language_switcher')

            <div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
                {{ __('Welcome to our website!') }}
            </div>
        </div>
    </div> <script data-no-optimize="1">window.lazyLoadOptions=Object.assign({},{threshold:300},window.lazyLoadOptions||{});!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).LazyLoad=e()}(this,function(){"use strict";function e(){return(e=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n,a=arguments[e];for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(t[n]=a[n])}return t}).apply(this,arguments)}function o(t){return e({},at,t)}function l(t,e){return t.getAttribute(gt+e)}function c(t){return l(t,vt)}function s(t,e){return function(t,e,n){e=gt+e;null!==n?t.setAttribute(e,n):t.removeAttribute(e)}(t,vt,e)}function i(t){return s(t,null),0}function r(t){return null===c(t)}function u(t){return c(t)===_t}function d(t,e,n,a){t&&(void 0===a?void 0===n?t(e):t(e,n):t(e,n,a))}function f(t,e){et?t.classList.add(e):t.className+=(t.className?" ":"")+e}function _(t,e){et?t.classList.remove(e):t.className=t.className.replace(new RegExp("(^|\\s+)"+e+"(\\s+|$)")," ").replace(/^\s+/,"").replace(/\s+$/,"")}function g(t){return t.llTempImage}function v(t,e){!e||(e=e._observer)&&e.unobserve(t)}function b(t,e){t&&(t.loadingCount+=e)}function p(t,e){t&&(t.toLoadCount=e)}function n(t){for(var e,n=[],a=0;e=t.children[a];a+=1)"SOURCE"===e.tagName&&n.push(e);return n}function h(t,e){(t=t.parentNode)&&"PICTURE"===t.tagName&&n(t).forEach(e)}function a(t,e){n(t).forEach(e)}function m(t){return!!t[lt]}function E(t){return t[lt]}function I(t){return delete t[lt]}function y(e,t){var n;m(e)||(n={},t.forEach(function(t){n[t]=e.getAttribute(t)}),e[lt]=n)}function L(a,t){var o;m(a)&&(o=E(a),t.forEach(function(t){var e,n;e=a,(t=o[n=t])?e.setAttribute(n,t):e.removeAttribute(n)}))}function k(t,e,n){f(t,e.class_loading),s(t,st),n&&(b(n,1),d(e.callback_loading,t,n))}function A(t,e,n){n&&t.setAttribute(e,n)}function O(t,e){A(t,rt,l(t,e.data_sizes)),A(t,it,l(t,e.data_srcset)),A(t,ot,l(t,e.data_src))}function w(t,e,n){var a=l(t,e.data_bg_multi),o=l(t,e.data_bg_multi_hidpi);(a=nt&&o?o:a)&&(t.style.backgroundImage=a,n=n,f(t=t,(e=e).class_applied),s(t,dt),n&&(e.unobserve_completed&&v(t,e),d(e.callback_applied,t,n)))}function x(t,e){!e||0<e.loadingCount||0<e.toLoadCount||d(t.callback_finish,e)}function M(t,e,n){t.addEventListener(e,n),t.llEvLisnrs[e]=n}function N(t){return!!t.llEvLisnrs}function z(t){if(N(t)){var e,n,a=t.llEvLisnrs;for(e in a){var o=a[e];n=e,o=o,t.removeEventListener(n,o)}delete t.llEvLisnrs}}function C(t,e,n){var a;delete t.llTempImage,b(n,-1),(a=n)&&--a.toLoadCount,_(t,e.class_loading),e.unobserve_completed&&v(t,n)}function R(i,r,c){var l=g(i)||i;N(l)||function(t,e,n){N(t)||(t.llEvLisnrs={});var a="VIDEO"===t.tagName?"loadeddata":"load";M(t,a,e),M(t,"error",n)}(l,function(t){var e,n,a,o;n=r,a=c,o=u(e=i),C(e,n,a),f(e,n.class_loaded),s(e,ut),d(n.callback_loaded,e,a),o||x(n,a),z(l)},function(t){var e,n,a,o;n=r,a=c,o=u(e=i),C(e,n,a),f(e,n.class_error),s(e,ft),d(n.callback_error,e,a),o||x(n,a),z(l)})}function T(t,e,n){var a,o,i,r,c;t.llTempImage=document.createElement("IMG"),R(t,e,n),m(c=t)||(c[lt]={backgroundImage:c.style.backgroundImage}),i=n,r=l(a=t,(o=e).data_bg),c=l(a,o.data_bg_hidpi),(r=nt&&c?c:r)&&(a.style.backgroundImage='url("'.concat(r,'")'),g(a).setAttribute(ot,r),k(a,o,i)),w(t,e,n)}function G(t,e,n){var a;R(t,e,n),a=e,e=n,(t=Et[(n=t).tagName])&&(t(n,a),k(n,a,e))}function D(t,e,n){var a;a=t,(-1<It.indexOf(a.tagName)?G:T)(t,e,n)}function S(t,e,n){var a;t.setAttribute("loading","lazy"),R(t,e,n),a=e,(e=Et[(n=t).tagName])&&e(n,a),s(t,_t)}function V(t){t.removeAttribute(ot),t.removeAttribute(it),t.removeAttribute(rt)}function j(t){h(t,function(t){L(t,mt)}),L(t,mt)}function F(t){var e;(e=yt[t.tagName])?e(t):m(e=t)&&(t=E(e),e.style.backgroundImage=t.backgroundImage)}function P(t,e){var n;F(t),n=e,r(e=t)||u(e)||(_(e,n.class_entered),_(e,n.class_exited),_(e,n.class_applied),_(e,n.class_loading),_(e,n.class_loaded),_(e,n.class_error)),i(t),I(t)}function U(t,e,n,a){var o;n.cancel_on_exit&&(c(t)!==st||"IMG"===t.tagName&&(z(t),h(o=t,function(t){V(t)}),V(o),j(t),_(t,n.class_loading),b(a,-1),i(t),d(n.callback_cancel,t,e,a)))}function $(t,e,n,a){var o,i,r=(i=t,0<=bt.indexOf(c(i)));s(t,"entered"),f(t,n.class_entered),_(t,n.class_exited),o=t,i=a,n.unobserve_entered&&v(o,i),d(n.callback_enter,t,e,a),r||D(t,n,a)}function q(t){return t.use_native&&"loading"in HTMLImageElement.prototype}function H(t,o,i){t.forEach(function(t){return(a=t).isIntersecting||0<a.intersectionRatio?$(t.target,t,o,i):(e=t.target,n=t,a=o,t=i,void(r(e)||(f(e,a.class_exited),U(e,n,a,t),d(a.callback_exit,e,n,t))));var e,n,a})}function B(e,n){var t;tt&&!q(e)&&(n._observer=new IntersectionObserver(function(t){H(t,e,n)},{root:(t=e).container===document?null:t.container,rootMargin:t.thresholds||t.threshold+"px"}))}function J(t){return Array.prototype.slice.call(t)}function K(t){return t.container.querySelectorAll(t.elements_selector)}function Q(t){return c(t)===ft}function W(t,e){return e=t||K(e),J(e).filter(r)}function X(e,t){var n;(n=K(e),J(n).filter(Q)).forEach(function(t){_(t,e.class_error),i(t)}),t.update()}function t(t,e){var n,a,t=o(t);this._settings=t,this.loadingCount=0,B(t,this),n=t,a=this,Y&&window.addEventListener("online",function(){X(n,a)}),this.update(e)}var Y="undefined"!=typeof window,Z=Y&&!("onscroll"in window)||"undefined"!=typeof navigator&&/(gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent),tt=Y&&"IntersectionObserver"in window,et=Y&&"classList"in document.createElement("p"),nt=Y&&1<window.devicePixelRatio,at={elements_selector:".lazy",container:Z||Y?document:null,threshold:300,thresholds:null,data_src:"src",data_srcset:"srcset",data_sizes:"sizes",data_bg:"bg",data_bg_hidpi:"bg-hidpi",data_bg_multi:"bg-multi",data_bg_multi_hidpi:"bg-multi-hidpi",data_poster:"poster",class_applied:"applied",class_loading:"litespeed-loading",class_loaded:"litespeed-loaded",class_error:"error",class_entered:"entered",class_exited:"exited",unobserve_completed:!0,unobserve_entered:!1,cancel_on_exit:!0,callback_enter:null,callback_exit:null,callback_applied:null,callback_loading:null,callback_loaded:null,callback_error:null,callback_finish:null,callback_cancel:null,use_native:!1},ot="src",it="srcset",rt="sizes",ct="poster",lt="llOriginalAttrs",st="loading",ut="loaded",dt="applied",ft="error",_t="native",gt="data-",vt="ll-status",bt=[st,ut,dt,ft],pt=[ot],ht=[ot,ct],mt=[ot,it,rt],Et={IMG:function(t,e){h(t,function(t){y(t,mt),O(t,e)}),y(t,mt),O(t,e)},IFRAME:function(t,e){y(t,pt),A(t,ot,l(t,e.data_src))},VIDEO:function(t,e){a(t,function(t){y(t,pt),A(t,ot,l(t,e.data_src))}),y(t,ht),A(t,ct,l(t,e.data_poster)),A(t,ot,l(t,e.data_src)),t.load()}},It=["IMG","IFRAME","VIDEO"],yt={IMG:j,IFRAME:function(t){L(t,pt)},VIDEO:function(t){a(t,function(t){L(t,pt)}),L(t,ht),t.load()}},Lt=["IMG","IFRAME","VIDEO"];return t.prototype={update:function(t){var e,n,a,o=this._settings,i=W(t,o);{if(p(this,i.length),!Z&&tt)return q(o)?(e=o,n=this,i.forEach(function(t){-1!==Lt.indexOf(t.tagName)&&S(t,e,n)}),void p(n,0)):(t=this._observer,o=i,t.disconnect(),a=t,void o.forEach(function(t){a.observe(t)}));this.loadAll(i)}},destroy:function(){this._observer&&this._observer.disconnect(),K(this._settings).forEach(function(t){I(t)}),delete this._observer,delete this._settings,delete this.loadingCount,delete this.toLoadCount},loadAll:function(t){var e=this,n=this._settings;W(t,n).forEach(function(t){v(t,e),D(t,n,e)})},restoreAll:function(){var e=this._settings;K(e).forEach(function(t){P(t,e)})}},t.load=function(t,e){e=o(e);D(t,e)},t.resetStatus=function(t){i(t)},t}),function(t,e){"use strict";function n(){e.body.classList.add("litespeed_lazyloaded")}function a(){console.log("[LiteSpeed] Start Lazy Load"),o=new LazyLoad(Object.assign({},t.lazyLoadOptions||{},{elements_selector:"[data-lazyloaded]",callback_finish:n})),i=function(){o.update()},t.MutationObserver&&new MutationObserver(i).observe(e.documentElement,{childList:!0,subtree:!0,attributes:!0})}var o,i;t.addEventListener?t.addEventListener("load",a,!1):t.attachEvent("onload",a)}(window,document);</script><script data-no-optimize="1">window.litespeed_ui_events=window.litespeed_ui_events||["mouseover","click","keydown","wheel","touchmove","touchstart"];var urlCreator=window.URL||window.webkitURL;function litespeed_load_delayed_js_force(){console.log("[LiteSpeed] Start Load JS Delayed"),litespeed_ui_events.forEach(e=>{window.removeEventListener(e,litespeed_load_delayed_js_force,{passive:!0})}),document.querySelectorAll("iframe[data-litespeed-src]").forEach(e=>{e.setAttribute("src",e.getAttribute("data-litespeed-src"))}),"loading"==document.readyState?window.addEventListener("DOMContentLoaded",litespeed_load_delayed_js):litespeed_load_delayed_js()}litespeed_ui_events.forEach(e=>{window.addEventListener(e,litespeed_load_delayed_js_force,{passive:!0})});async function litespeed_load_delayed_js(){let t=[];for(var d in document.querySelectorAll('script[type="litespeed/javascript"]').forEach(e=>{t.push(e)}),t)await new Promise(e=>litespeed_load_one(t[d],e));document.dispatchEvent(new Event("DOMContentLiteSpeedLoaded")),window.dispatchEvent(new Event("DOMContentLiteSpeedLoaded"))}function litespeed_load_one(t,e){console.log("[LiteSpeed] Load ",t);var d=document.createElement("script");d.addEventListener("load",e),d.addEventListener("error",e),t.getAttributeNames().forEach(e=>{"type"!=e&&d.setAttribute("data-src"==e?"src":e,t.getAttribute(e))});let a=!(d.type="text/javascript");!d.src&&t.textContent&&(d.src=litespeed_inline2src(t.textContent),a=!0),t.after(d),t.remove(),a&&e()}function litespeed_inline2src(t){try{var d=urlCreator.createObjectURL(new Blob([t.replace(/^(?:<!--)?(.*?)(?:-->)?$/gm,"$1")],{type:"text/javascript"}))}catch(e){d="data:text/javascript;base64,"+btoa(t.replace(/^(?:<!--)?(.*?)(?:-->)?$/gm,"$1"))}return d}</script><script data-optimized="1" type="litespeed/javascript" data-src="https://www.linguise.com/wp-content/litespeed/js/dcfb0d8aef9b57b394f38c36a44631fb.js?ver=7271c"></script></body>


				
			

Откройте файл app/Providers/AppServiceProvider.php и добавьте следующий код в метод boot(), чтобы при использовании переключателя языка текущая локаль отображалась во всех представлениях

				
					* Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    view()->composer('partials.language_switcher', function ($view) {
        $view->with('current_locale', app()->getLocale());
        $view->with('available_locales', config('app.available_locales'));
    });
}


				
			

Расширенные возможности перевода в Laravel PHP

Иллюстрация, изображающая людей, работающих над проектом. Команда, сотрудничающая друг с другом.

В дальнейшем мы рассмотрим другие компоненты локализации, а именно дату, число и валюту. Вот шаги, которые необходимо выполнить.

Локализованные даты в Laravel

Работа с датами и временем имеет решающее значение в процессе локализации. Laravel использует Carbon для управления датами и временем. Вот как можно использовать Carbon для отображения локализованной даты.

				
					<?php
Route::get('/', function () {
    $today = \Carbon\Carbon::now()
        ->settings(
            [
                'locale' => app()->getLocale(),
            ]
        );

    // LL is macro placeholder for MMMM D, YYYY (you could write same as dddd, MMMM D, YYYY)
    $dateMessage = $today->isoFormat('dddd, LL');

    return view('welcome', [
        'date_message' => $dateMessage
    ]);
});


				
			

Этот код устанавливает локаль Carbon на основе текущей локали приложения и соответствующим образом форматирует дату.

Чтобы отобразить локализованную дату в представлении:

				
					{{ __('Welcome to our website, :Name', ['name' => ‘Johb’]) }}
<br>
{{ trans_choice('{0} There :form :count apples|{1} There :form just :count apple|[2,19] There :form :count apples', 1, ['form' => 'is']) }}
<br>
{{ $date_message }}


				
			

Форматирование чисел и валют

В разных странах существуют различные способы форматирования чисел. Например….

  • Франция → 123 123,12
  • Германия → 123.123,12
  • Япония → 123,123

Для учета этих различий в вашем приложении Laravel вы можете использовать NumberFormatter.

				
					<?php
$num = NumberFormatter::create('en_US', NumberFormatter::DECIMAL);

$num2 = NumberFormatter::create('fr', NumberFormatter::DECIMAL);
				
			

Также можно писать цифры на определённом языке.

				
					<?php
$num = NumberFormatter::create('en_US', NumberFormatter::SPELLOUT);

$num2 = NumberFormatter::create('it', NumberFormatter::SPELLOUT);
				
			

Вот список валют. Для французской локали (`fr`) валюта будет отображаться в евро (€), а для американской локали (`en_US`) — в долларах США ($).

				
					<?php
$currency1 = NumberFormatter::create('it', NumberFormatter::CURRENCY);

$currency2 = NumberFormatter::create('en_US', NumberFormatter::CURRENCY);
				
			
Преодолейте языковые барьеры
Попрощайтесь с языковыми барьерами и приветствуйте безграничный рост! Попробуйте наш автоматический перевод сегодня.

Альтернативное решение: локализация Laravel с помощью Linguise

Команда работает над цифровым проектом. Их окружают различные цифровые инструменты и символы.

После ознакомления с этапами локализации Laravel , описанными в статье, следует отметить, что этот процесс включает в себя множество шагов, требующих от пользователей глубокого понимания программного кода Laravel .

Это, безусловно, может создать трудности для начинающих пользователей, желающих локализовать свои приложения. Поэтому необходимо более инновационное решение, способное к быстрому переводу, поддерживающее локализацию и требующее всего нескольких простых шагов для внедрения.

Одним из перспективных решений является Linguise. Linguise предлагает более простой и эффективный подход к локализации Laravel , не требующий глубоких знаний в программировании. Ключевые особенности Linguise включают в себя:.

  • Простая интеграция с Laravel
  • Настройте переключатель языков без программирования
  • Перевод изображений
  • Редактор в реальном времени для настройки переводов под местный контекст
  • Динамический перевод для динамически генерируемого контента
  • SEO-оптимизация для многоязычных версий

Установка Linguise на веб-сайты Linguise Laravel довольно проста. Вот краткое объяснение.

  1. Создайте учетную запись Linguise (воспользуйтесь 30-дневной бесплатной пробной версией)
  2. Зарегистрируйте свой веб-домен Laravel и введите некоторую информацию. Вы получите ключ API.
  3. Загрузите и подключите скрипт перевода Linguise к полученной папке Laravel .
  4. Укажите языковые URL-адреса в файле .htaccess.
  5. Вставьте скрипт переключения языка в раздел <head> вашего HTML-кода.
  6. Настройте переключатель языков по своему усмотрению
  7. В веб-интерфейсе Laravel появится переключатель языка, и контент будет переведен автоматически.

Как это работает? С Linguiseвам нужно всего лишь зарегистрироваться и активировать приложение, и появится переключатель языков. После этого вы сможете локализовать контент, например, с помощью редактора в реальном времени, переводить медиафайлы, изображения и т. д.

Готовы осваивать новые рынки? Попробуйте наш автоматический перевод бесплатно с нашей 1-месячной пробной версией без риска. Кредитная карта не нужна!

Вывод

Локализация Laravel — это мощная функция, позволяющая разработчикам создавать многоязычные веб-сайты и приложения. Как мы уже видели, встроенный процесс локализации Laravel включает в себя несколько этапов и требует хорошего понимания фреймворка. Это может быть непросто для новичков или тех, кто ищет более быстрое решение.

Такие инструменты, как Linguise предлагают инновационную альтернативу для тех, кто ищет более оптимизированный подход. Эти решения обеспечивают быструю передачу данных, простую интеграцию и удобные функции, такие как настраиваемые переключатели языков и перевод изображений. Создайте свою учетную запись Linguise и воспользуйтесь нашей функцией локализации вашего Laravel !

Вам также может быть интересно прочитать

Не пропустите!
Подписывайтесь на нашу новостную рассылку

Получайте новости об автоматическом переводе сайтов, международной SEO-оптимизации и многом другом!

Invalid email address
Попробуйте. Одна подписка в месяц, и вы можете отписаться в любое время.

Не уходите, не поделившись своей электронной почтой!

Мы не можем гарантировать, что вы выиграете в лотерею, но можем обещать интересные информационные новости о переводах и периодические скидки.

Не пропустите!
Invalid email address