دليل التوطين خطوة بخطوة [ Laravel ]

ارتقِ بأعمالك إلى المستوى التالي من خلال التوسع العالمي. نمو الأعمال وتطويرها.
جدول المحتويات

Laravel هو أداة قوية تمكن المطورين من إنشاء مواقع متعددة اللغات. من خلال تنفيذ توطين الويب في Laravel مشاريعك ، يمكنك توفير تجربة مستخدم شخصية للزوار من خلفيات لغوية مختلفة ، مما يؤدي في النهاية إلى توسيع نطاق وصولك وتحسين تفاعل المستخدم.

سنرشد بك عبر تنفيذ الترجمة في تطبيقك Laravel وتقديم أداة يمكنها تبسيط وتعزيز جهود الترجمة الخاصة بك!

لماذا يجب عليك ترجمة موقع Laravel ؟

رجل يرتدي سترة برتقالية يحمل جهازًا لوحيًا مع خريطة العالم. يتم عرض أعلام دول مختلفة.

هنا بعض الأسباب الهامة لترجمة موقعك Laravel .

  • الوصول إلى جمهور عالمي: من خلال ترجمة موقعك Laravel
  • تحسين تجربة المستخدم:
  • ميزة تنافسية: في سوق عالمي تنافسي، يمكن أن يوفر تقديم التطبيقات بلغات متعددة ميزة كبيرة على المنافسين. يظهر التزامك بالأسواق الدولية ويمكن أن يساعدك في دخول أسواق جديدة بشكل أكثر فعالية.
  • تحسين محركات البحث: تميل المواقع الإلكترونية المترجمة جيدًا إلى الترتيب بشكل أفضل في محركات البحث للبحث بلغة معينة. يمكن أن يؤدي ذلك إلى زيادة حركة مرور مدونتك والظهور عبر الإنترنت في أسواق مختلفة باستخدام الترجمة متعددة اللغات.

متطلبات ترجمة متعددة اللغات لـ Laravel

شخصان يتعاونان على شاشة كمبيوتر كبيرة مع تطبيقات مختلفة. يبدو أنهما يعملان معًا.

هناك بعض المتطلبات والخطوات التي يجب مراعاتها عند تنفيذ الترجمة متعددة اللغات في Laravel.

  • للحصول على أحدث ميزات الترجمة المحلية ، تأكد من أنك تستخدم أحدث إصدار من Laravel (على سبيل المثال ، الإصدار 10.x).
  • سيكون الفهم الأساسي لـ PHP وإطار عمل Laravel مفيدًا في عملية التنفيذ.
  • إعداد بيئة تطوير محلية أو خادم يدعم Laravel، بما في ذلك خادم ويب وقاعدة بيانات.
  • تحديد اللغات التي سيتم دعمها في تطبيقك منذ البداية.

ترجمات Laravel بسيطة

رجل يعمل على الكمبيوتر المحمول. تصفح الويب على الشاشة.

بعد فهم المتطلبات التي يجب الوفاء بها قبل ترجمة تطبيق Laravel أو الويب ، سنقدم بعض الخطوات لترجمة Laravel ببساطة.

لقيام بذلك ، افتح ملف العرض الذي تريد ترجمته ، على سبيل المثال ، resources/views/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">
            <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||(sessionStorage.getItem("litespeed_reloaded")?console.log("LiteSpeed: skipping guest vary reload (already reloaded this session)"):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),sessionStorage.setItem("litespeed_reloaded","1"),window.location.reload(!0))}));</script><script data-optimized="1" type="litespeed/javascript" data-src="https://www.linguise.com/wp-content/litespeed/js/6a930764693d3e1670931eab6a4d8a7e.js?ver=76fa0"></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',

				
			

يجب أن يكون الشرح أعلاه المفاتيح واضحًا. باختصار ، يحتوي مفتاح اللغة على اللغة الافتراضية لتطبيقك (إذا لم يتم تحديد لغة أخرى في الكود). يتم تنشيط fallback_locale إذا تم طلب لغة غير موجودة في تطبيقك.

الآن ، دعنا نضيف مفتاحًا جديدًا إلى هذا الملف لتوفير قائمة بجميع اللغات المدعومة.

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

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


				
			

في هذه المرحلة ، حاولنا Laravel ويب لدعم ثلاث لغات ، وهي الإنجليزية والإيطالية والفرنسية.

نظرة عامة على ملفات ترجمة Laravel

امرأة تشير إلى شاشة كمبيوتر كبيرة تعرض أيقونات ملونة مختلفة. الشاشة لها خلفية بيضاء مع أيقونات زرقاء و أرجوانية.

في لارافيل، كما هو الحال في العديد من الأطر الأخرى، يتم تخزين الترجمات للغات مختلفة في ملفات منفصلة. يتم استخدام طريقتين لتنظيم ملفات الترجمة هذه.

الطريقة القديمة تخزن الملفات في الهيكل التالي: resources/lang/{en,fr,it}/{myfile.php}. الطريقة الأحدث تستخدم ملفات JSON، مثل resources/lang/{fr.json, it.json}. ستركز هذه المقالة على الطريقة الأحدث، على الرغم من أن المبادئ متشابهة للطريقة القديمة، باستثناء الاختلافات في كيفية تسمية مفاتيح الترجمة والوصول إليها.

للغات ذات الاختلافات الإقليمية، يجب تسمية أدلة اللغة أو الملفات وفقًا لمعيار ISO 15897. على سبيل المثال، سيتم تسمية الإنجليزية البريطانية en_GB بدلاً من en-gb.

معلومات عامة

في لارافيل، كما هو الحال مع العديد من الأطر، يتم تخزين الترجمات للغات مختلفة في ملفات منفصلة. هناك طريقتان رئيسيتان لتنظيم ملفات ترجمة لارافيل.

  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}. سيتم حفظ المنطقة المحددة في الجلسة ، وسيتم إعادة توجيه المستخدمين إلى صفحتهم السابقة (كما تم التعامل معها بواسطة Localization middleware). 

لاختباره، انتقل إلى localhost/language/it (بافتراض أن ملف تعريف الارتباط للجلسة نشط في متصفحك)، ويجب أن ترى المحتوى المترجم. يمكنك التنقل في الموقع أو تحديث الصفحة، وستظل اللغة المختارة سارية المفعول.

تنفيذ مفتاح اللغة

الآن، نحن بحاجة إلى تزويد المستخدم بخيار قابل للنقر لتغيير مفتاح اللغة 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/6a930764693d3e1670931eab6a4d8a7e.js?ver=76fa0"></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 كاربون لإدارة التواريخ والأوقات. إليك كيفية استخدام كاربون لعرض تاريخ مترجم محليًا.

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


				
			

يحدد هذا الكود لغة كاربون بناءً على اللغة الحالية للتطبيق ويصيغ التاريخ وفقًا لذلك.

لعرض التاريخ المترجم في عرض:

				
					{{ __('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
  • تخصيص مفتاح اللغة بدون ترميز
  • ترجمة الصور
  • محرر مباشر لتخصيص الترجمات حسب السياق المحلي
  • ترجمة ديناميكية للمحتوى المولد ديناميكيًا
  • تحسين محركات البحث للإصدارات متعددة اللغات

خطوات تثبيت Linguise على Laravel مواقع الويب يمكن القيام بها بسهولة أيضًا. إليك شرحًا موجزًا.

  1. إنشاء حساب Linguise (استخدم التجربة المجانية لمدة 30 يومًا مجانًا)
  2. سجل نطاق الويب الخاص بك Laravel وأدخل بعض المعلومات. سوف تحصل على مفتاح API.
  3. قم بتحميل وتوصيل برنامج الترجمة Linguise إلى مجلد Laravel الذي حصلت عليه.
  4. إعداد عناوين URL للغة في ملف htaccess.
  5. أدخل برنامج نصي تبديل اللغة في رأس HTML الخاص بك.
  6. تخصيص مفتاح تبديل اللغة حسب الحاجة
  7. سيظهر مفتاح تبديل اللغة على الويب Laravel ، ويمكن ترجمة المحتوى تلقائيًا.

كيف؟ باستخدام Linguise، ما عليك سوى التسجيل والتفعيل، وسيظهر مفتاح تبديل اللغة. بعد ذلك، يمكنك التحرير بحرية، على سبيل المثال، من خلال المحرر المباشر، وترجمة الوسائط والصور، إلخ.

هل أنت مستعد لاستكشاف أسواق جديدة؟ جرب خدمة الترجمة التلقائية لدينا مجانًا مع تجربة شهرية خالية من المخاطر. لا حاجة لبطاقة ائتمان!

الاستنتاج

توطين Laravel هو ميزة قوية تمكن المطورين من إنشاء مواقع ويب وتطبيقات متعددة اللغات. كما رأينا، تتضمن عملية التوطين المدمجة لـ Laravel خطوات متعددة وتتطلب فهمًا جيدًا للإطار. قد يكون ذلك تحديًا للمبتدئين أو أولئك الذين يبحثون عن حل أسرع.

توفر أدوات مثل Linguise بديلاً مبتكرًا لمن يسعون إلى نهج أكثر بساطة. تقدم هذه الحلول قدرات ترجمة سريعة ، وتكاملًا سهلًا ، وميزات سهلة الاستخدام مثل مفاتيح تبديل اللغة القابلة للتخصيص وترجمة الصور. الآن ، إنشاء حسابك Linguise واستمتع بميزتنا لتوطين Laravel

قد تكون مهتمًا أيضًا بقراءة

لا تفوت الفرصة!
اشترك في نشرتنا الإخبارية

تلقي الأخبار حول الترجمة التلقائية للموقع، وتحسين محركات البحث الدولية، والمزيد!

Invalid email address
جربها. مرة واحدة في الشهر، ويمكنك إلغاء الاشتراك في أي وقت.

لا تغادر دون مشاركة بريدك الإلكتروني!

لا يمكننا أن نضمن فوزك باليانصيب، ولكن يمكننا أن نعدك ببعض الأخبار الإعلامية المثيرة للاهتمام حول الترجمة وبعض الخصومات العرضية.

لا تفوت الفرصة!
Invalid email address