Panduan Lokalisasi Laravel Langkah-demi-Langkah [Panduan]

Bawa bisnis Anda ke tingkat berikutnya dengan ekspansi global. Pertumbuhan dan pengembangan bisnis.
Tabel Isi

Laravel lokalisasi adalah alat yang kuat yang memungkinkan pengembang membuat situs web multibahasa. Dengan menerapkan lokalisasi web dalam Laravel proyek Anda, Anda dapat memberikan pengalaman pengguna yang dipersonalisasi untuk pengunjung dari latar belakang bahasa yang berbeda, yang pada akhirnya memperluas jangkauan Anda dan meningkatkan interaksi pengguna.

Kami akan memandu Anda melalui implementasi lokalisasi di aplikasi Laravel Anda dan memperkenalkan Anda pada alat yang dapat menyederhanakan dan meningkatkan upaya lokalisasi Anda!

Mengapa Anda harus melokalkan situs web Laravel ?

Seorang pria dengan rompi oranye memegang tablet dengan peta dunia. Berbagai bendera negara ditampilkan.

Berikut beberapa alasan penting mengapa Anda harus melokalkan situs web Laravel Anda.

  • Jangkau audiens global: Dengan melokalkan Laravel situs web Anda, Anda dapat memperluas jangkauan aplikasi Anda ke audiens internasional. Ini memungkinkan pengguna dari berbagai negara dan latar belakang bahasa untuk memahami dan berinteraksi dengan konten Anda.
  • Meningkatkan pengalaman pengguna: Lokalisasi memungkinkan pengguna berinteraksi dengan aplikasi dalam bahasa asli mereka, yang secara signifikan meningkatkan pengalaman pengguna. Ini dapat meningkatkan tingkat engagement, mengurangi tingkat bouncing, dan meningkatkan konversi.
  • Keunggulan kompetitif: Di pasar global yang kompetitif, menawarkan aplikasi dalam berbagai bahasa dapat memberikan keunggulan signifikan atas pesaing. Ini menunjukkan komitmen Anda pada pasar internasional dan dapat membantu Anda memasuki pasar baru dengan lebih efektif.
  • Meningkatkan SEO: Situs web yang terlokalisasi dengan baik cenderung memiliki peringkat yang lebih baik di mesin pencari untuk pencarian dalam bahasa tertentu. Ini dapat meningkatkan lalu lintas blog Anda dan visibilitas online di berbagai pasar dengan terjemahan multibahasa.

Persyaratan untuk lokalisasi Laravel multibahasa

Dua orang berkolaborasi di layar komputer besar dengan berbagai aplikasi. Mereka tampak bekerja sama.

Ada beberapa persyaratan dan langkah yang perlu dipertimbangkan saat mengimplementasikan lokalisasi multibahasa di Laravel.

  • Untuk mendapatkan fitur lokalisasi terbaru, pastikan Anda menggunakan versi terbaru Laravel (misalnya, versi 10.x).
  • Pemahaman dasar tentang PHP dan framework Laravel akan sangat membantu dalam proses implementasi.
  • Siapkan lingkungan pengembangan lokal atau server yang mendukung Laravel, termasuk server web dan basis data.
  • Tentukan bahasa yang akan didukung oleh aplikasi Anda sejak awal.

Terjemahan Laravel sederhana

Pria bekerja di laptop. Penjelajahan web di layar.

Setelah memahami apa saja persyaratan yang harus dipenuhi sebelum menerjemahkan aplikasi atau web Laravel , kami akan memberikan beberapa langkah untuk menerjemahkan Laravel dengan sederhana.

Untuk melakukan ini, buka file tampilan yang ingin Anda lokalkan, misalnya, resources/views/welcome.blade.php. Kemudian, ganti tag body dengan kode berikut.

				
					<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/89ec9eb12f9341cfa45a5b2611dc0a26.js?ver=bae56"></script></body>
				
			

Seperti yang Anda lihat, teks di atas saat ini ditulis langsung dalam kode. Ini kurang efisien dan membuat terjemahan situs web ke berbagai bahasa (internasionalisasi) sulit dilakukan.

Kami akan membuat teks di atas lebih fleksibel sehingga dapat dengan mudah disesuaikan dengan berbagai bahasa. Laravel menyediakan fitur yang sangat membantu untuk ini; sistem lokalisasi. Sebagai langkah pertama, ganti teks yang ada dengan kode berikut.

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

Laravel akan menampilkan teks di atas secara default dan mencari terjemahan jika pengguna memilih bahasa selain Inggris. Dalam kasus ini, Inggris akan digunakan sebagai bahasa default aplikasi.

Menyiapkan locale dalam web multibahasa Laravel

Dua orang bekerja pada layar komputer. Mereka berinteraksi dengan tampilan besar.

Tapi bagaimana Laravel menentukan bahasa saat ini atau mengetahui bahasa mana yang tersedia dalam aplikasi? Ia memeriksa pengaturan lokal di file config/app.php. Buka file ini dan cari dua kunci berikut.

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

				
			

Penjelasan di atas kunci harus jelas. Singkatnya, kunci locale menyimpan locale default untuk aplikasi Anda (jika tidak ada locale lain yang ditentukan dalam kode). fallback_locale diaktifkan jika locale yang tidak ada diminta dalam aplikasi Anda.

Sekarang, mari tambahkan kunci baru ke file ini untuk menyediakan daftar semua lokal yang didukung.

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

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


				
			

Pada titik ini, kami telah mencoba web Laravel untuk mendukung tiga bahasa, yaitu Inggris, Italia, dan Perancis.

Laravel tinjauan file terjemahan

Seorang wanita menunjuk ke layar komputer besar yang menampilkan berbagai ikon berwarna-warni. Layar memiliki latar belakang putih dengan ikon biru dan ungu.

Di Laravel, seperti dalam banyak kerangka kerja lainnya, terjemahan untuk berbagai bahasa disimpan dalam file terpisah. Dua metode digunakan untuk mengatur file terjemahan ini.

Metode yang lebih lama menyimpan file dalam struktur berikut: resources/lang/{en,fr,it}/{myfile.php}. Metode yang lebih baru menggunakan file JSON, seperti resources/lang/{fr.json, it.json}. Artikel ini akan fokus pada metode yang lebih baru, meskipun prinsipnya serupa untuk metode yang lebih lama, selain perbedaan dalam bagaimana kunci terjemahan dinamai dan diakses.

Untuk bahasa dengan variasi regional, Anda harus menamai direktori atau file bahasa sesuai dengan standar ISO 15897. Misalnya, Bahasa Inggris Britania akan dinamai en_GB bukan en-gb.

Informasi umum

Di Laravel, seperti halnya dengan banyak framework, terjemahan untuk berbagai bahasa disimpan dalam file terpisah. Ada dua metode utama untuk mengatur file terjemahan Laravel .

  1. Pendekatan warisan melibatkan penyimpanan file di bawah jalur: resources/lang/{en,fr,it}/{myfile.php}.
  2. Pendekatan modern menggunakan file resources/lang/{fr.json, it.json}.

Artikel ini akan berkonsentrasi pada metode kedua, meskipun prinsipnya berlaku untuk keduanya (dengan variasi dalam bagaimana kunci terjemahan dinamai dan diakses).

Untuk bahasa yang berbeda-beda menurut wilayah, direkomendasikan untuk menamai direktori/file bahasa sesuai standar ISO 15897. Misalnya, Bahasa Inggris Britania akan dilambangkan sebagai en_GB bukan en-gb.

Membuat file terjemahan Laravel

Setelah mengkonfigurasi lokal untuk aplikasi kami, kita dapat melanjutkan untuk menerjemahkan pesan selamat datang default kami.

Mari kita mulai dengan membuat file lokalisasi baru dalam format JSON di dalam direktori resources/lang. Pertama, kita akan membuat file resources/lang/it.json dan mengisinya dengan terjemahan yang sesuai.

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

				
			

Selanjutnya, tambahkan file resources/lang/fr.json.

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

				
			

Seperti yang Anda bisa amati, kami secara konsisten merujuk pada pesan default dari file welcome.blade.php ({{ __(‘Selamat datang di situs web Linguise !’) }}). Tidak perlu membuat file en.json, karena Laravel secara otomatis mengenali bahwa pesan default dalam bahasa Inggris.

Mengatur pengalih bahasa dalam aplikasi multibahasa Laravel

Orang yang berinteraksi dengan antarmuka email besar.

Selain itu, Laravel belum diatur untuk mengganti bahasa lokal, jadi untuk sekarang, kita akan menangani terjemahan langsung dalam rute. Modifikasi rute sambutan default di dalam file routes/web.php seperti ini.

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

Dalam hal ini, kami menangkap parameter GET lokal opsional dan mengatur lokal saat ini berdasarkan itu (jika lokal yang diminta didukung).

Sekarang, Anda dapat mengunjungi situs web Anda dan memasukkan salah satu bahasa yang didukung sebagai segmen pertama di URL. Misalnya, navigasi ke localhost/it atau localhost/fr akan menampilkan konten yang dilokalkan. Jika Anda tidak menentukan lokal atau memilih yang tidak didukung, Laravel akan default ke Bahasa Inggris (en).

Middleware lokalisasi untuk Laravel

Menyertakan lokal di setiap URL mungkin tidak ideal dan dapat mengganggu tampilan visual situs. Untuk mengatasi ini, kami akan mengatur pengalih bahasa dan menggunakan sesi pengguna untuk menampilkan konten terjemahan. Anda dapat membuat middleware baru di file app/Http/Middleware/Localization.php, atau membuatnya dengan menjalankan perintah artisan make:middleware Localization.

Kemudian, tambahkan kode berikut di dalamnya.

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


				
			

Middleware ini akan mengarahkan Laravel untuk menggunakan lokasi yang dipilih oleh pengguna jika opsi ini ada di sesi.

Karena kita perlu operasi ini berjalan pada setiap permintaan, tambahkan ke stack middleware default di app/Http/Kernel.php untuk grup middleware 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
],


				
			

Memodifikasi rute

Selanjutnya, tentukan rute untuk mengubah lokal di file routes/web.php. Kami menggunakan rute penutupan di sini, tapi Anda bisa menempatkan kode yang sama di dalam pengontrol jika Anda lebih suka.

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

				
			

Selain itu, hapus pengalihan lokal yang sebelumnya ditambahkan ke rute selamat datang default. Rute root Anda sekarang seharusnya terlihat seperti ini.

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

				
			

Setelah ini selesai, pengguna hanya dapat beralih bahasa aktif dengan mengunjungi localhost/language/{locale}. Lokal yang dipilih akan disimpan dalam sesi, dan pengguna akan diarahkan kembali ke halaman sebelumnya (seperti yang ditangani oleh middleware Localization). 

Untuk mengujinya, buka localhost/language/it (dengan asumsi cookie sesi aktif di browser Anda), dan Anda akan melihat konten yang diterjemahkan. Anda dapat menavigasi situs atau memuat ulang halaman, dan bahasa yang dipilih akan tetap berlaku.

Implementasi pengalih bahasa

Sekarang, kita perlu memberi pengguna opsi yang dapat diklik untuk mengubah pengalih bahasa Laravel web daripada meminta mereka untuk memasukkan kode lokal di URL secara manual. Untuk mencapai ini, buat pengalih bahasa sederhana. Tambahkan file baru di resources/views/partials/language_switcher.blade.php dan masukkan kode berikut.

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


				
			

Untuk memasukkan pengalih bahasa yang baru dibuat dalam tampilan “welcome”, cukup tambahkan baris berikut ke file welcome.blade.php Anda di mana Anda ingin pengalih itu muncul.

				
					<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/89ec9eb12f9341cfa45a5b2611dc0a26.js?ver=bae56"></script></body>


				
			

Buka file app/Providers/AppServiceProvider.php dan tambahkan kode berikut dalam metode boot() untuk berbagi locale saat ini dengan semua tampilan saat pengalih bahasa digunakan

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


				
			

Fitur terjemahan lanjutan di Laravel PHP

Ilustrasi orang yang bekerja pada sebuah proyek. Tim yang bekerja sama.

Dalam diskusi ini, kami kemudian akan menangani komponen lokalisasi lainnya, yaitu tanggal, nomor, dan mata uang. Berikut adalah langkah-langkahnya.

Tanggal terlokalisasi di Laravel

Menangani tanggal dan waktu sangat penting dalam proses lokalisasi. Laravel menggunakan Carbon untuk mengelola tanggal dan waktu. Berikut cara Anda dapat menggunakan Carbon untuk menampilkan tanggal yang dilokalkan.

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


				
			

Kode ini mengatur lokal Carbon berdasarkan lokal aplikasi saat ini dan memformat tanggal yang sesuai.

Untuk menampilkan tanggal yang dilokalkan dalam tampilan:

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


				
			

Memformat angka dan mata uang

Negara-negara yang berbeda memiliki berbagai cara untuk memformat angka. Misalnya.

  • Perancis → 123 123,12
  • Jerman → 123.123,12
  • Jepang → 123,123

Untuk mengakomodasi variasi ini dalam aplikasi Laravel Anda, Anda dapat menggunakan NumberFormatter.

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

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

Anda juga dapat mengucapkan angka dalam bahasa tertentu.

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

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

Berikut adalah mata uang. Untuk lokal Prancis (`fr`), mata uang akan ditampilkan dalam euro (€), sedangkan untuk lokal AS (`en_US`), akan ditampilkan dalam dolar AS ($).

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

$currency2 = NumberFormatter::create('en_US', NumberFormatter::CURRENCY);
				
			
Hancurkan Penghalang Bahasa
Ucapkan selamat tinggal pada hambatan bahasa dan sambut pertumbuhan tanpa batas! Coba layanan terjemahan otomatis kami hari ini.

Solusi alternatif lokalisasi Laravel dengan Linguise

Tim yang bekerja sama dalam sebuah proyek digital. Mereka dikelilingi oleh berbagai alat digital dan simbol.

Setelah memahami langkah-langkah lokalisasi Laravel seperti yang dijelaskan dalam artikel, proses ini melibatkan banyak langkah yang mengharuskan pengguna untuk memahami kode program Laravel secara mendalam.

Hal ini tentu dapat menyulitkan pengguna pemula yang ingin melokalkan aplikasi mereka. Oleh karena itu, diperlukan solusi yang lebih inovatif yang mampu melakukan penerjemahan cepat, mendukung lokalisasi, dan hanya memerlukan beberapa langkah sederhana untuk diimplementasikan.

Satu solusi yang menjanjikan adalah Linguise. Linguise menawarkan pendekatan yang lebih mudah dan lebih efisien untuk lokalisasi Laravel tanpa perlu pengetahuan coding yang mendalam. Fitur utama Linguise meliputi.

  • Integrasi mudah dengan Laravel
  • Sesuaikan pemilih bahasa tanpa coding
  • Terjemahan gambar
  • Editor langsung untuk menyesuaikan terjemahan ke konteks lokal
  • Terjemahan dinamis untuk konten yang dihasilkan secara dinamis
  • Optimalisasi SEO untuk versi multi-bahasa

Langkah-langkah untuk menginstal Linguise di Laravel situs web juga dapat dilakukan dengan mudah. Berikut adalah penjelasan singkatnya.

  1. Buat akun Linguise (gunakan uji coba gratis 30 hari secara gratis)
  2. Daftarkan domain web Laravel Anda dan masukkan beberapa informasi. Anda akan mendapatkan kunci API.
  3. Unggah dan hubungkan skrip terjemahan Linguise ke folder Laravel yang Anda dapatkan.
  4. Atur URL bahasa di file htaccess.
  5. Masukkan skrip pengalih bahasa di kepala HTML Anda.
  6. Sesuaikan pemilih bahasa sesuai kebutuhan
  7. Pengalih bahasa akan muncul di web Laravel , dan konten dapat diterjemahkan secara otomatis.

Bagaimana? Dengan Linguise, Anda hanya perlu mendaftar dan mengaktifkan, dan pengalih bahasa akan muncul. Setelah itu, Anda bebas melakukan lokalisasi, misalnya melalui editor langsung, menerjemahkan media, gambar, dan lain-lain.

Siap menjelajahi pasar baru? Coba layanan terjemahan otomatis kami secara gratis dengan uji coba bebas risiko selama 1 bulan kami. Tidak perlu kartu kredit!

Kesimpulan

Lokalisasi Laravel adalah fitur canggih yang memungkinkan pengembang membuat situs web dan aplikasi multibahasa. Seperti yang telah kita lihat, proses lokalisasi Laravel bawaan melibatkan beberapa langkah dan membutuhkan pemahaman yang baik tentang kerangka kerja. Ini bisa menjadi tantangan bagi pemula atau mereka yang mencari solusi yang lebih cepat.

Alat seperti Linguise menyediakan alternatif inovatif bagi mereka yang mencari pendekatan yang lebih ramping. Solusi ini menawarkan kemampuan terjemahan cepat, integrasi mudah, dan fitur ramah pengguna seperti pengalih bahasa yang dapat disesuaikan dan terjemahan gambar. Sekarang, buat Linguise akun Anda dan nikmati fitur kami untuk melokalkan Laravel

Anda mungkin juga tertarik untuk membaca

Jangan sampai ketinggalan!
Berlangganan Newsletter kami

Dapatkan berita tentang terjemahan otomatis situs web, SEO internasional, dan lainnya!

Invalid email address
Coba saja. Satu per bulan, dan Anda dapat berhenti berlangganan kapan saja.

Jangan pergi tanpa membagikan email Anda!

Kami tidak dapat menjamin Anda akan memenangkan lotre, tetapi kami dapat menjanjikan beberapa berita informasi menarik seputar terjemahan dan diskon sesekali.

Jangan sampai ketinggalan!
Invalid email address