Lokalizacja Laravel krok po kroku [Przewodnik]

Lokalizacja Laravel krok po kroku [Przewodnik]
Spis treści

Laravel to potężne narzędzie, które umożliwia programistom tworzenie wielojęzycznych stron internetowych. Wdrażając lokalizację internetową w swoich Laravel , możesz zapewnić spersonalizowaną obsługę użytkownikom z różnych środowisk językowych, ostatecznie zwiększając swój zasięg i poprawiając zaangażowanie użytkowników.

Przeprowadzimy Cię przez proces wdrażania lokalizacji w Twojej aplikacji Laravel i przedstawimy narzędzie, które może uprościć i usprawnić Twoje wysiłki lokalizacyjne!

Dlaczego warto zlokalizować stronę Laravel ?

Lokalizacja Laravel krok po kroku [Przewodnik]

Oto kilka ważnych powodów, dla których warto zlokalizować swoją witrynę Laravel .

  • Dotrzyj do odbiorców na całym świecie: lokalizując swoją Laravel , możesz rozszerzyć zasięg swojej aplikacji na odbiorców międzynarodowych. Dzięki temu użytkownicy z różnych krajów i języków mogą zrozumieć Twoje treści i wchodzić z nimi w interakcję.
  • Poprawia komfort użytkowania: Lokalizacja umożliwia użytkownikom interakcję z aplikacją w ich ojczystym języku, co znacznie poprawia komfort użytkowania. Może to zwiększyć współczynnik zaangażowania, zmniejszyć współczynnik odrzuceń i zwiększyć liczbę konwersji.
  • Przewaga konkurencyjna: na konkurencyjnym rynku globalnym oferowanie aplikacji w wielu językach może zapewnić znaczną przewagę nad konkurencją. Pokazuje Twoje zaangażowanie na rynkach międzynarodowych i może pomóc Ci skuteczniej wejść na nowe rynki.
  • Poprawia SEO: Dobrze zlokalizowane witryny internetowe mają tendencję do lepszej pozycji w wyszukiwarkach w przypadku wyszukiwań w określonym języku. Może to zwiększyć ruch na Twoim blogu i widoczność w Internecie na różnych rynkach dzięki tłumaczeniu wielojęzycznemu.

Wymagania dla wielojęzycznej lokalizacji Laravel

Lokalizacja Laravel krok po kroku [Przewodnik]

Istnieje kilka wymagań i kroków, które należy wziąć pod uwagę podczas wdrażania lokalizacji wielojęzycznej w Laravel.

  • Aby uzyskać najnowsze funkcje lokalizacyjne, upewnij się, że używasz najnowszej wersji Laravel (np. wersji 10.x).
  • W procesie wdrożenia pomocna będzie podstawowa znajomość PHP i frameworku Laravel .
  • Skonfiguruj lokalne środowisko programistyczne lub serwer obsługujący Laravel, w tym serwer WWW i bazę danych.
  • Od początku określ języki, które będzie obsługiwała Twoja aplikacja.

Proste tłumaczenia Laravel

Lokalizacja Laravel krok po kroku [Przewodnik]

Po zrozumieniu, jakie wymagania należy spełnić przed tłumaczeniem aplikacji lub sieci Laravel , przedstawimy kilka kroków umożliwiających proste tłumaczenie Laravel .

Aby to zrobić, otwórz plik widoku, który chcesz zlokalizować, na przykład Resources/views/welcome.blade.php. Następnie zastąp znacznik treści następującym kodem.

				
					<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">!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 i(t){return e({},it,t)}function o(t,e){var n,a="LazyLoad::Initialized",i=new t(e);try{n=new CustomEvent(a,{detail:{instance:i}})}catch(t){(n=document.createEvent("CustomEvent")).initCustomEvent(a,!1,!1,{instance:i})}window.dispatchEvent(n)}function l(t,e){return t.getAttribute(gt+e)}function c(t){return l(t,bt)}function s(t,e){return function(t,e,n){e=gt+e;null!==n?t.setAttribute(e,n):t.removeAttribute(e)}(t,bt,e)}function r(t){return s(t,null),0}function u(t){return null===c(t)}function d(t){return c(t)===vt}function f(t,e,n,a){t&&(void 0===a?void 0===n?t(e):t(e,n):t(e,n,a))}function _(t,e){nt?t.classList.add(e):t.className+=(t.className?" ":"")+e}function v(t,e){nt?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 b(t,e){!e||(e=e._observer)&&e.unobserve(t)}function p(t,e){t&&(t.loadingCount+=e)}function h(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 m(t,e){(t=t.parentNode)&&"PICTURE"===t.tagName&&n(t).forEach(e)}function a(t,e){n(t).forEach(e)}function E(t){return!!t[st]}function I(t){return t[st]}function y(t){return delete t[st]}function A(e,t){var n;E(e)||(n={},t.forEach(function(t){n[t]=e.getAttribute(t)}),e[st]=n)}function k(a,t){var i;E(a)&&(i=I(a),t.forEach(function(t){var e,n;e=a,(t=i[n=t])?e.setAttribute(n,t):e.removeAttribute(n)}))}function L(t,e,n){_(t,e.class_loading),s(t,ut),n&&(p(n,1),f(e.callback_loading,t,n))}function w(t,e,n){n&&t.setAttribute(e,n)}function x(t,e){w(t,ct,l(t,e.data_sizes)),w(t,rt,l(t,e.data_srcset)),w(t,ot,l(t,e.data_src))}function O(t,e,n){var a=l(t,e.data_bg_multi),i=l(t,e.data_bg_multi_hidpi);(a=at&&i?i:a)&&(t.style.backgroundImage=a,n=n,_(t=t,(e=e).class_applied),s(t,ft),n&&(e.unobserve_completed&&b(t,e),f(e.callback_applied,t,n)))}function N(t,e){!e||0<e.loadingCount||0<e.toLoadCount||f(t.callback_finish,e)}function C(t,e,n){t.addEventListener(e,n),t.llEvLisnrs[e]=n}function M(t){return!!t.llEvLisnrs}function z(t){if(M(t)){var e,n,a=t.llEvLisnrs;for(e in a){var i=a[e];n=e,i=i,t.removeEventListener(n,i)}delete t.llEvLisnrs}}function R(t,e,n){var a;delete t.llTempImage,p(n,-1),(a=n)&&--a.toLoadCount,v(t,e.class_loading),e.unobserve_completed&&b(t,n)}function T(o,r,c){var l=g(o)||o;M(l)||function(t,e,n){M(t)||(t.llEvLisnrs={});var a="VIDEO"===t.tagName?"loadeddata":"load";C(t,a,e),C(t,"error",n)}(l,function(t){var e,n,a,i;n=r,a=c,i=d(e=o),R(e,n,a),_(e,n.class_loaded),s(e,dt),f(n.callback_loaded,e,a),i||N(n,a),z(l)},function(t){var e,n,a,i;n=r,a=c,i=d(e=o),R(e,n,a),_(e,n.class_error),s(e,_t),f(n.callback_error,e,a),i||N(n,a),z(l)})}function G(t,e,n){var a,i,o,r,c;t.llTempImage=document.createElement("IMG"),T(t,e,n),E(c=t)||(c[st]={backgroundImage:c.style.backgroundImage}),o=n,r=l(a=t,(i=e).data_bg),c=l(a,i.data_bg_hidpi),(r=at&&c?c:r)&&(a.style.backgroundImage='url("'.concat(r,'")'),g(a).setAttribute(ot,r),L(a,i,o)),O(t,e,n)}function D(t,e,n){var a;T(t,e,n),a=e,e=n,(t=It[(n=t).tagName])&&(t(n,a),L(n,a,e))}function V(t,e,n){var a;a=t,(-1<yt.indexOf(a.tagName)?D:G)(t,e,n)}function F(t,e,n){var a;t.setAttribute("loading","lazy"),T(t,e,n),a=e,(e=It[(n=t).tagName])&&e(n,a),s(t,vt)}function j(t){t.removeAttribute(ot),t.removeAttribute(rt),t.removeAttribute(ct)}function P(t){m(t,function(t){k(t,Et)}),k(t,Et)}function S(t){var e;(e=At[t.tagName])?e(t):E(e=t)&&(t=I(e),e.style.backgroundImage=t.backgroundImage)}function U(t,e){var n;S(t),n=e,u(e=t)||d(e)||(v(e,n.class_entered),v(e,n.class_exited),v(e,n.class_applied),v(e,n.class_loading),v(e,n.class_loaded),v(e,n.class_error)),r(t),y(t)}function $(t,e,n,a){var i;n.cancel_on_exit&&(c(t)!==ut||"IMG"===t.tagName&&(z(t),m(i=t,function(t){j(t)}),j(i),P(t),v(t,n.class_loading),p(a,-1),r(t),f(n.callback_cancel,t,e,a)))}function q(t,e,n,a){var i,o,r=(o=t,0<=pt.indexOf(c(o)));s(t,"entered"),_(t,n.class_entered),v(t,n.class_exited),i=t,o=a,n.unobserve_entered&&b(i,o),f(n.callback_enter,t,e,a),r||V(t,n,a)}function H(t){return t.use_native&&"loading"in HTMLImageElement.prototype}function B(t,i,o){t.forEach(function(t){return(a=t).isIntersecting||0<a.intersectionRatio?q(t.target,t,i,o):(e=t.target,n=t,a=i,t=o,void(u(e)||(_(e,a.class_exited),$(e,n,a,t),f(a.callback_exit,e,n,t))));var e,n,a})}function J(e,n){var t;et&&!H(e)&&(n._observer=new IntersectionObserver(function(t){B(t,e,n)},{root:(t=e).container===document?null:t.container,rootMargin:t.thresholds||t.threshold+"px"}))}function K(t){return Array.prototype.slice.call(t)}function Q(t){return t.container.querySelectorAll(t.elements_selector)}function W(t){return c(t)===_t}function X(t,e){return e=t||Q(e),K(e).filter(u)}function Y(e,t){var n;(n=Q(e),K(n).filter(W)).forEach(function(t){v(t,e.class_error),r(t)}),t.update()}function t(t,e){var n,a,t=i(t);this._settings=t,this.loadingCount=0,J(t,this),n=t,a=this,Z&&window.addEventListener("online",function(){Y(n,a)}),this.update(e)}var Z="undefined"!=typeof window,tt=Z&&!("onscroll"in window)||"undefined"!=typeof navigator&&/(gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent),et=Z&&"IntersectionObserver"in window,nt=Z&&"classList"in document.createElement("p"),at=Z&&1<window.devicePixelRatio,it={elements_selector:".lazy",container:tt||Z?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",rt="srcset",ct="sizes",lt="poster",st="llOriginalAttrs",ut="loading",dt="loaded",ft="applied",_t="error",vt="native",gt="data-",bt="ll-status",pt=[ut,dt,ft,_t],ht=[ot],mt=[ot,lt],Et=[ot,rt,ct],It={IMG:function(t,e){m(t,function(t){A(t,Et),x(t,e)}),A(t,Et),x(t,e)},IFRAME:function(t,e){A(t,ht),w(t,ot,l(t,e.data_src))},VIDEO:function(t,e){a(t,function(t){A(t,ht),w(t,ot,l(t,e.data_src))}),A(t,mt),w(t,lt,l(t,e.data_poster)),w(t,ot,l(t,e.data_src)),t.load()}},yt=["IMG","IFRAME","VIDEO"],At={IMG:P,IFRAME:function(t){k(t,ht)},VIDEO:function(t){a(t,function(t){k(t,ht)}),k(t,mt),t.load()}},kt=["IMG","IFRAME","VIDEO"];return t.prototype={update:function(t){var e,n,a,i=this._settings,o=X(t,i);{if(h(this,o.length),!tt&&et)return H(i)?(e=i,n=this,o.forEach(function(t){-1!==kt.indexOf(t.tagName)&&F(t,e,n)}),void h(n,0)):(t=this._observer,i=o,t.disconnect(),a=t,void i.forEach(function(t){a.observe(t)}));this.loadAll(o)}},destroy:function(){this._observer&&this._observer.disconnect(),Q(this._settings).forEach(function(t){y(t)}),delete this._observer,delete this._settings,delete this.loadingCount,delete this.toLoadCount},loadAll:function(t){var e=this,n=this._settings;X(t,n).forEach(function(t){b(t,e),V(t,n,e)})},restoreAll:function(){var e=this._settings;Q(e).forEach(function(t){U(t,e)})}},t.load=function(t,e){e=i(e);V(t,e)},t.resetStatus=function(t){r(t)},Z&&function(t,e){if(e)if(e.length)for(var n,a=0;n=e[a];a+=1)o(t,n);else o(t,e)}(t,window.lazyLoadOptions),t});!function(e,t){"use strict";function a(){t.body.classList.add("litespeed_lazyloaded")}function n(){console.log("[LiteSpeed] Start Lazy Load Images"),d=new LazyLoad({elements_selector:"[data-lazyloaded]",callback_finish:a}),o=function(){d.update()},e.MutationObserver&&new MutationObserver(o).observe(t.documentElement,{childList:!0,subtree:!0,attributes:!0})}var d,o;e.addEventListener?e.addEventListener("load",n,!1):e.attachEvent("onload",n)}(window,document);</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/435d0eaada3b593adf726e5cdfb5dd43.js?ver=0e199"></script><script>const 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></body>
				
			

Jak widać, powyższy tekst jest obecnie zapisany bezpośrednio w kodzie. Jest to mniej wydajne i utrudnia tłumaczenie stron internetowych na różne języki (internacjonalizacja).

Zamierzamy uelastycznić powyższy tekst, aby można go było łatwo dostosować do różnych języków. Laravel zapewnia w tym celu bardzo przydatną funkcję; systemu lokalizacji. W pierwszym kroku zastąp istniejący tekst następującym kodem.

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

Laravel domyślnie wyświetli powyższy tekst i sprawdzi tłumaczenie, jeśli użytkownik wybierze język inny niż angielski. W takim przypadku domyślnym językiem aplikacji będzie język angielski.

Konfigurowanie ustawień regionalnych w wielojęzycznej sieci Laravel

Lokalizacja Laravel krok po kroku [Przewodnik]

Ale w jaki sposób Laravel określa aktualny język lub wie, jakie języki są dostępne w aplikacji? Sprawdza ustawienia regionalne w pliku config/app.php. Otwórz ten plik i poszukaj następujących dwóch kluczy.

				
					/*
|--------------------------------------------------------------------------
| 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',

				
			

Wyjaśnienie nad klawiszami powinno być jasne. Podsumowując, klucz ustawień regionalnych przechowuje domyślne ustawienia regionalne dla Twojej aplikacji (jeśli w kodzie nie określono innych ustawień regionalnych). Fallback_locale jest aktywowane, jeśli w aplikacji zażądano nieistniejących ustawień regionalnych.

Dodajmy teraz nowy klucz do tego pliku, aby wyświetlić listę wszystkich obsługiwanych ustawień regionalnych.

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

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


				
			

W tym momencie wypróbowaliśmy Laravel Web obsługującą trzy języki, a mianowicie angielski, włoski i francuski.

Przegląd plików tłumaczeń Laravel

Lokalizacja Laravel krok po kroku [Przewodnik]

W Laravel, podobnie jak w wielu innych frameworkach, tłumaczenia na różne języki przechowywane są w oddzielnych plikach. Do organizowania plików tłumaczeń stosowane są dwie metody.

Starsza metoda przechowuje pliki w następującej strukturze: Resources/lang/{en,fr,it}/{myfile.php}. Nowsza metoda wykorzystuje pliki JSON, takie jak Resources/lang/{fr.json, it.json}. W tym artykule skupimy się na nowszej metodzie, chociaż zasady są podobne w przypadku starszej metody, poza różnicami w sposobie nazywania kluczy tłumaczeniowych i uzyskiwaniu do nich dostępu.

W przypadku języków różniących się regionalnie katalogi lub pliki językowe należy nazwać zgodnie z normą ISO 15897. Na przykład brytyjski angielski miałby nazwę en_GB zamiast en-gb.

Informacje ogólne

W Laravel, podobnie jak w wielu frameworkach, tłumaczenia na różne języki przechowywane są w oddzielnych plikach. Istnieją dwie podstawowe metody porządkowania plików tłumaczeń Laravel .

  1. Starsze podejście polega na przechowywaniu plików w ścieżce: Resources/lang/{en,fr,it}/{myfile.php}.
  2. Nowoczesne podejście wykorzystuje pliki Resources/lang/{fr.json, it.json}.

W tym artykule skoncentrujemy się na drugiej metodzie, chociaż zasady mają zastosowanie do obu (z różnicami w sposobie nazywania kluczy tłumaczeniowych i uzyskiwaniu do nich dostępu).

W przypadku języków różniących się w zależności od regionu zaleca się nadawanie nazw katalogom/plikom językowym zgodnie ze standardami ISO 15897. Na przykład brytyjski angielski będzie oznaczany jako en_GB, a nie en-gb.

Tworzenie plików tłumaczeniowych Laravel

Po skonfigurowaniu ustawień regionalnych dla naszej aplikacji możemy przystąpić do tłumaczenia naszej domyślnej wiadomości powitalnej.

Zacznijmy od utworzenia nowych plików lokalizacyjnych w formacie JSON w katalogu Resources/lang. Najpierw utworzymy plik Resources/lang/it.json i wypełnimy go odpowiednimi tłumaczeniami.

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

				
			

Następnie dodaj plik Resources/lang/fr.json.

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

				
			

Jak widać, konsekwentnie odwołujemy się do domyślnej wiadomości z pliku Welcome.blade.php ({{ __('Witamy w witrynie Linguise !') }}). Nie ma potrzeby tworzenia pliku en.json, ponieważ Laravel automatycznie rozpoznaje, że domyślne wiadomości są w języku angielskim.

Ustawianie przełącznika języka w wielojęzycznej aplikacji Laravel

Lokalizacja Laravel krok po kroku [Przewodnik]

Co więcej, Laravel nie jest jeszcze skonfigurowany do zastępowania języka lokalnego, więc na razie zajmiemy się tłumaczeniem bezpośrednio w ramach trasy. Zmodyfikuj domyślną trasę powitalną w pliku Routes/web.php w ten sposób.

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

W tym przypadku przechwytujemy opcjonalny parametr GET ustawień regionalnych i na jego podstawie ustawiamy bieżące ustawienia regionalne (jeśli obsługiwane są żądane ustawienia regionalne).

Teraz możesz odwiedzić swoją witrynę i dodać dowolny z obsługiwanych języków jako pierwszy segment adresu URL. Na przykład przejście do localhost/it lub localhost/fr spowoduje wyświetlenie zlokalizowanej zawartości. Jeśli nie określisz ustawień regionalnych lub wybierzesz takie, które nie jest obsługiwane, Laravel domyślnie ustawi język angielski (en).

Oprogramowanie pośredniczące w lokalizacji dla Laravel

Uwzględnianie ustawień regionalnych w każdym adresie URL może nie być idealnym rozwiązaniem i może zaburzyć atrakcyjność wizualną witryny. Aby rozwiązać ten problem, skonfigurujemy przełącznik języka i wykorzystamy sesję użytkownika do wyświetlenia przetłumaczonej treści. Możesz utworzyć nowe oprogramowanie pośrednie w pliku app/Http/Middleware/Localization.php lub wygenerować je, uruchamiając polecenie rzemieślnika make:middleware Localization.

Następnie dodaj w środku następujący kod.

				
					<?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);
    }
}


				
			

To oprogramowanie pośredniczące pokieruje Laravel do użycia lokalizacji wybranej przez użytkownika, jeśli ta opcja jest dostępna w sesji.

Ponieważ operacja musi być wykonywana przy każdym żądaniu, dodaj ją do domyślnego stosu oprogramowania pośredniego w pliku app/Http/Kernel.php dla grupy oprogramowania pośredniego sieci Web.

				
					/**
* 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
],


				
			

Modyfikowanie tras

Następnie zdefiniuj trasę do zmiany ustawień regionalnych w pliku Routes/web.php. Używamy tutaj trasy zamknięcia, ale jeśli wolisz, możesz umieścić ten sam kod w kontrolerze.

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

				
			

Ponadto usuń zmianę ustawień regionalnych, która została wcześniej dodana do domyślnej trasy powitalnej. Twoja trasa główna powinna teraz wyglądać tak.

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

				
			

Po wykonaniu tej czynności użytkownik może zmienić aktywny język jedynie odwiedzając localhost/language/{locale}. Wybrane ustawienia regionalne zostaną zapisane w sesji, a użytkownicy zostaną przekierowani z powrotem na poprzednią stronę (zgodnie z obsługą oprogramowania pośredniczącego lokalizacji). 

Aby to przetestować, przejdź do localhost/language/it (zakładając, że plik cookie sesji jest aktywny w Twojej przeglądarce) i powinieneś zobaczyć przetłumaczoną treść. Możesz poruszać się po witrynie lub odświeżyć stronę, a wybrany język pozostanie ważny.

Implementacja przełącznika języka

Teraz musimy zapewnić użytkownikowi klikalną opcję zmiany przełącznika języka Laravel web, zamiast wymagać od niego ręcznego wprowadzania kodów regionalnych w adresie URL. Aby to osiągnąć, utwórz prosty przełącznik języków. Dodaj nowy plik pod adresem Resources/views/partials/language_switcher.blade.php i wstaw następujący kod.

				
					<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>


				
			

Aby uwzględnić nowo utworzony przełącznik języka w widoku „powitalnym”, po prostu dodaj następujący wiersz do pliku powitalnego.blade.php, w którym chcesz, aby przełącznik się pojawił.

				
					<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">!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 i(t){return e({},it,t)}function o(t,e){var n,a="LazyLoad::Initialized",i=new t(e);try{n=new CustomEvent(a,{detail:{instance:i}})}catch(t){(n=document.createEvent("CustomEvent")).initCustomEvent(a,!1,!1,{instance:i})}window.dispatchEvent(n)}function l(t,e){return t.getAttribute(gt+e)}function c(t){return l(t,bt)}function s(t,e){return function(t,e,n){e=gt+e;null!==n?t.setAttribute(e,n):t.removeAttribute(e)}(t,bt,e)}function r(t){return s(t,null),0}function u(t){return null===c(t)}function d(t){return c(t)===vt}function f(t,e,n,a){t&&(void 0===a?void 0===n?t(e):t(e,n):t(e,n,a))}function _(t,e){nt?t.classList.add(e):t.className+=(t.className?" ":"")+e}function v(t,e){nt?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 b(t,e){!e||(e=e._observer)&&e.unobserve(t)}function p(t,e){t&&(t.loadingCount+=e)}function h(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 m(t,e){(t=t.parentNode)&&"PICTURE"===t.tagName&&n(t).forEach(e)}function a(t,e){n(t).forEach(e)}function E(t){return!!t[st]}function I(t){return t[st]}function y(t){return delete t[st]}function A(e,t){var n;E(e)||(n={},t.forEach(function(t){n[t]=e.getAttribute(t)}),e[st]=n)}function k(a,t){var i;E(a)&&(i=I(a),t.forEach(function(t){var e,n;e=a,(t=i[n=t])?e.setAttribute(n,t):e.removeAttribute(n)}))}function L(t,e,n){_(t,e.class_loading),s(t,ut),n&&(p(n,1),f(e.callback_loading,t,n))}function w(t,e,n){n&&t.setAttribute(e,n)}function x(t,e){w(t,ct,l(t,e.data_sizes)),w(t,rt,l(t,e.data_srcset)),w(t,ot,l(t,e.data_src))}function O(t,e,n){var a=l(t,e.data_bg_multi),i=l(t,e.data_bg_multi_hidpi);(a=at&&i?i:a)&&(t.style.backgroundImage=a,n=n,_(t=t,(e=e).class_applied),s(t,ft),n&&(e.unobserve_completed&&b(t,e),f(e.callback_applied,t,n)))}function N(t,e){!e||0<e.loadingCount||0<e.toLoadCount||f(t.callback_finish,e)}function C(t,e,n){t.addEventListener(e,n),t.llEvLisnrs[e]=n}function M(t){return!!t.llEvLisnrs}function z(t){if(M(t)){var e,n,a=t.llEvLisnrs;for(e in a){var i=a[e];n=e,i=i,t.removeEventListener(n,i)}delete t.llEvLisnrs}}function R(t,e,n){var a;delete t.llTempImage,p(n,-1),(a=n)&&--a.toLoadCount,v(t,e.class_loading),e.unobserve_completed&&b(t,n)}function T(o,r,c){var l=g(o)||o;M(l)||function(t,e,n){M(t)||(t.llEvLisnrs={});var a="VIDEO"===t.tagName?"loadeddata":"load";C(t,a,e),C(t,"error",n)}(l,function(t){var e,n,a,i;n=r,a=c,i=d(e=o),R(e,n,a),_(e,n.class_loaded),s(e,dt),f(n.callback_loaded,e,a),i||N(n,a),z(l)},function(t){var e,n,a,i;n=r,a=c,i=d(e=o),R(e,n,a),_(e,n.class_error),s(e,_t),f(n.callback_error,e,a),i||N(n,a),z(l)})}function G(t,e,n){var a,i,o,r,c;t.llTempImage=document.createElement("IMG"),T(t,e,n),E(c=t)||(c[st]={backgroundImage:c.style.backgroundImage}),o=n,r=l(a=t,(i=e).data_bg),c=l(a,i.data_bg_hidpi),(r=at&&c?c:r)&&(a.style.backgroundImage='url("'.concat(r,'")'),g(a).setAttribute(ot,r),L(a,i,o)),O(t,e,n)}function D(t,e,n){var a;T(t,e,n),a=e,e=n,(t=It[(n=t).tagName])&&(t(n,a),L(n,a,e))}function V(t,e,n){var a;a=t,(-1<yt.indexOf(a.tagName)?D:G)(t,e,n)}function F(t,e,n){var a;t.setAttribute("loading","lazy"),T(t,e,n),a=e,(e=It[(n=t).tagName])&&e(n,a),s(t,vt)}function j(t){t.removeAttribute(ot),t.removeAttribute(rt),t.removeAttribute(ct)}function P(t){m(t,function(t){k(t,Et)}),k(t,Et)}function S(t){var e;(e=At[t.tagName])?e(t):E(e=t)&&(t=I(e),e.style.backgroundImage=t.backgroundImage)}function U(t,e){var n;S(t),n=e,u(e=t)||d(e)||(v(e,n.class_entered),v(e,n.class_exited),v(e,n.class_applied),v(e,n.class_loading),v(e,n.class_loaded),v(e,n.class_error)),r(t),y(t)}function $(t,e,n,a){var i;n.cancel_on_exit&&(c(t)!==ut||"IMG"===t.tagName&&(z(t),m(i=t,function(t){j(t)}),j(i),P(t),v(t,n.class_loading),p(a,-1),r(t),f(n.callback_cancel,t,e,a)))}function q(t,e,n,a){var i,o,r=(o=t,0<=pt.indexOf(c(o)));s(t,"entered"),_(t,n.class_entered),v(t,n.class_exited),i=t,o=a,n.unobserve_entered&&b(i,o),f(n.callback_enter,t,e,a),r||V(t,n,a)}function H(t){return t.use_native&&"loading"in HTMLImageElement.prototype}function B(t,i,o){t.forEach(function(t){return(a=t).isIntersecting||0<a.intersectionRatio?q(t.target,t,i,o):(e=t.target,n=t,a=i,t=o,void(u(e)||(_(e,a.class_exited),$(e,n,a,t),f(a.callback_exit,e,n,t))));var e,n,a})}function J(e,n){var t;et&&!H(e)&&(n._observer=new IntersectionObserver(function(t){B(t,e,n)},{root:(t=e).container===document?null:t.container,rootMargin:t.thresholds||t.threshold+"px"}))}function K(t){return Array.prototype.slice.call(t)}function Q(t){return t.container.querySelectorAll(t.elements_selector)}function W(t){return c(t)===_t}function X(t,e){return e=t||Q(e),K(e).filter(u)}function Y(e,t){var n;(n=Q(e),K(n).filter(W)).forEach(function(t){v(t,e.class_error),r(t)}),t.update()}function t(t,e){var n,a,t=i(t);this._settings=t,this.loadingCount=0,J(t,this),n=t,a=this,Z&&window.addEventListener("online",function(){Y(n,a)}),this.update(e)}var Z="undefined"!=typeof window,tt=Z&&!("onscroll"in window)||"undefined"!=typeof navigator&&/(gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent),et=Z&&"IntersectionObserver"in window,nt=Z&&"classList"in document.createElement("p"),at=Z&&1<window.devicePixelRatio,it={elements_selector:".lazy",container:tt||Z?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",rt="srcset",ct="sizes",lt="poster",st="llOriginalAttrs",ut="loading",dt="loaded",ft="applied",_t="error",vt="native",gt="data-",bt="ll-status",pt=[ut,dt,ft,_t],ht=[ot],mt=[ot,lt],Et=[ot,rt,ct],It={IMG:function(t,e){m(t,function(t){A(t,Et),x(t,e)}),A(t,Et),x(t,e)},IFRAME:function(t,e){A(t,ht),w(t,ot,l(t,e.data_src))},VIDEO:function(t,e){a(t,function(t){A(t,ht),w(t,ot,l(t,e.data_src))}),A(t,mt),w(t,lt,l(t,e.data_poster)),w(t,ot,l(t,e.data_src)),t.load()}},yt=["IMG","IFRAME","VIDEO"],At={IMG:P,IFRAME:function(t){k(t,ht)},VIDEO:function(t){a(t,function(t){k(t,ht)}),k(t,mt),t.load()}},kt=["IMG","IFRAME","VIDEO"];return t.prototype={update:function(t){var e,n,a,i=this._settings,o=X(t,i);{if(h(this,o.length),!tt&&et)return H(i)?(e=i,n=this,o.forEach(function(t){-1!==kt.indexOf(t.tagName)&&F(t,e,n)}),void h(n,0)):(t=this._observer,i=o,t.disconnect(),a=t,void i.forEach(function(t){a.observe(t)}));this.loadAll(o)}},destroy:function(){this._observer&&this._observer.disconnect(),Q(this._settings).forEach(function(t){y(t)}),delete this._observer,delete this._settings,delete this.loadingCount,delete this.toLoadCount},loadAll:function(t){var e=this,n=this._settings;X(t,n).forEach(function(t){b(t,e),V(t,n,e)})},restoreAll:function(){var e=this._settings;Q(e).forEach(function(t){U(t,e)})}},t.load=function(t,e){e=i(e);V(t,e)},t.resetStatus=function(t){r(t)},Z&&function(t,e){if(e)if(e.length)for(var n,a=0;n=e[a];a+=1)o(t,n);else o(t,e)}(t,window.lazyLoadOptions),t});!function(e,t){"use strict";function a(){t.body.classList.add("litespeed_lazyloaded")}function n(){console.log("[LiteSpeed] Start Lazy Load Images"),d=new LazyLoad({elements_selector:"[data-lazyloaded]",callback_finish:a}),o=function(){d.update()},e.MutationObserver&&new MutationObserver(o).observe(t.documentElement,{childList:!0,subtree:!0,attributes:!0})}var d,o;e.addEventListener?e.addEventListener("load",n,!1):e.attachEvent("onload",n)}(window,document);</script><script data-optimized="1" type="litespeed/javascript" data-src="https://www.linguise.com/wp-content/litespeed/js/435d0eaada3b593adf726e5cdfb5dd43.js?ver=0e199"></script><script>const 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></body>


				
			

Otwórz plik app/Providers/AppServiceProvider.php i dodaj następujący kod w metodzie boot(), aby udostępnić bieżące ustawienia regionalne wszystkim widokom, gdy używany jest przełącznik języka

				
					* 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'));
    });
}


				
			

Zaawansowane funkcje tłumaczenia w Laravel PHP

Lokalizacja Laravel krok po kroku [Przewodnik]

W tej dyskusji zajmiemy się następnie innymi składnikami lokalizacji, a mianowicie datą, liczbą i walutą. Oto kroki.

Zlokalizowane daty w Laravel

Obsługa dat i godzin ma kluczowe znaczenie w procesie lokalizacji. Laravel wykorzystuje Carbon do zarządzania datami i godzinami. Oto, jak możesz użyć Carbon do wyświetlenia zlokalizowanej daty.

				
					<?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
    ]);
});


				
			

Ten kod ustawia ustawienia regionalne Carbon na podstawie bieżących ustawień regionalnych aplikacji i odpowiednio formatuje datę.

Aby wyświetlić zlokalizowaną datę w widoku:

				
					{{ __('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 }}


				
			

Formatowanie liczb i walut

W różnych krajach obowiązują różne sposoby formatowania liczb. Na przykład.

  • Francja → 123 123,12
  • Niemcy → 123.123,12
  • Japonia → 123123

Aby uwzględnić te różnice w aplikacji Laravel , możesz użyć NumberFormatter.

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

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

Możesz także przeliterować liczby w określonym języku.

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

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

Oto waluty. W przypadku ustawień regionalnych francuskich („fr”) waluta będzie wyświetlana w euro (€), natomiast w przypadku ustawień regionalnych amerykańskich („en_US”) będzie wyświetlana w dolarach amerykańskich ($).

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

$currency2 = NumberFormatter::create('en_US', NumberFormatter::CURRENCY);
				
			
Przełam bariery językowe
Pożegnaj się z barierami językowymi i przywitaj nieograniczony rozwój! Wypróbuj naszą usługę automatycznego tłumaczenia już dziś.

Alternatywne rozwiązanie Lokalizacja Laravel za pomocą Linguise

Lokalizacja Laravel krok po kroku [Przewodnik]

Po zrozumieniu etapów lokalizacji Laravel opisanych w artykule, proces ten obejmuje wiele kroków, które wymagają od użytkowników dogłębnego zrozumienia kodu programu Laravel .

Z pewnością może to utrudnić początkującym użytkownikom chcącym zlokalizować swoje aplikacje. Dlatego potrzebne jest bardziej innowacyjne rozwiązanie, które umożliwia szybkie tłumaczenie, wspiera lokalizację, a jego wdrożenie wymaga tylko kilku prostych kroków.

Jednym z obiecujących rozwiązań jest Linguise. Linguise oferuje łatwiejsze i bardziej wydajne podejście do lokalizacji Laravel bez konieczności posiadania dogłębnej wiedzy na temat kodowania. Kluczowe funkcje Linguise obejmują.

  • Łatwa integracja z Laravel
  • Dostosuj przełącznik języków bez kodowania
  • Tłumaczenie obrazu
  • Edytor na żywo umożliwiający dostosowywanie tłumaczeń do lokalnego kontekstu
  • Dynamiczne tłumaczenie dynamicznie generowanej treści
  • Optymalizacja SEO dla wersji wielojęzycznych

Kroki instalacji Linguise na stronach Laravel można również łatwo wykonać. Oto krótkie wyjaśnienie.

  1. Utwórz konto Linguise (skorzystaj z 30-dniowego bezpłatnego okresu próbnego za darmo)
  2. Zarejestruj swoją domenę internetową Laravel i wprowadź pewne informacje. Otrzymasz klucz API.
  3. Prześlij i podłącz skrypt tłumaczący Linguise do otrzymanego folderu Laravel .
  4. Skonfiguruj adresy URL języków w pliku htaccess.
  5. Wstaw skrypt przełączający język w nagłówku kodu HTML.
  6. Dostosuj przełącznik języka według potrzeb
  7. Przełącznik języka pojawi się w sieci Laravel , a treść będzie mogła zostać przetłumaczona automatycznie.

Jak? W Linguisewystarczy się zarejestrować i aktywować, a pojawi się przełącznik języka. Następnie możesz swobodnie lokalizować, na przykład za pomocą edytora na żywo, tłumaczyć multimedia, obrazy itp.

Gotowy na odkrywanie nowych rynków? Wypróbuj naszą usługę automatycznego tłumaczenia za darmo w ramach 1-miesięcznego okresu próbnego bez ryzyka. Karta kredytowa nie jest potrzebna!

Wniosek

Lokalizacja Laravel to potężna funkcja, która umożliwia programistom tworzenie wielojęzycznych witryn i aplikacji. Jak widzieliśmy, wbudowany proces lokalizacji Laravel obejmuje wiele kroków i wymaga dobrego zrozumienia frameworka. Może to stanowić wyzwanie dla początkujących lub osób poszukujących szybszego rozwiązania.

Narzędzia takie jak Linguise stanowią innowacyjną alternatywę dla osób poszukujących bardziej usprawnionego podejścia. Rozwiązania te oferują możliwości szybkiego tłumaczenia, łatwą integrację i przyjazne dla użytkownika funkcje, takie jak konfigurowalne przełączniki języków i tłumaczenie obrazów. Teraz utwórz swoje konto Linguise i skorzystaj z naszej funkcji lokalizowania Laravel !

Możesz być również zainteresowany czytaniem

Nie przegap!
Zapisz się do naszego newslettera

Otrzymuj informacje o automatycznym tłumaczeniu stron internetowych, międzynarodowym SEO i nie tylko!

Invalid email address
Spróbuj. Jeden miesięcznie i możesz zrezygnować z subskrypcji w dowolnym momencie.

Nie wychodź bez udostępnienia swojego e-maila!

Nie możemy zagwarantować, że wygrasz na loterii, ale możemy obiecać kilka interesujących wiadomości informacyjnych dotyczących tłumaczeń i okazjonalnych rabatów.

Nie przegap!
Invalid email address