ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ # Google Translate Implementation Strategy ## Cerro Corá Real Estate Website ### Table of Contents 1. [Project Overview](#project-overview) 2. [Current Project Analysis](#current-project-analysis) 3. [Implementation Strategy](#implementation-strategy) 4. [Phase-by-Phase Implementation](#phase-by-phase-implementation) 5. [Technical Implementation Details](#technical-implementation-details) 6. [Cost Analysis](#cost-analysis) 7. [Benefits and ROI](#benefits-and-roi) 8. [Maintenance and Support](#maintenance-and-support) --- ## Project Overview This document outlines a comprehensive strategy for implementing Google Translate functionality in the Cerro Corá real estate website built with Laravel 5.4. The implementation will enable automatic translation of content into multiple languages, improving user experience for international visitors and expanding the company's market reach. ### Objectives - Enable multi-language support for the real estate website - Implement automatic content translation using Google Translate API - Maintain performance and user experience - Provide admin tools for translation management - Ensure cost-effective implementation --- ## Current Project Analysis ### Technology Stack - **Framework**: Laravel 5.4 - **Database**: MySQL - **Current Locale**: Spanish (`es`) with English fallback (`en`) - **Timezone**: America/Asuncion (Paraguay) ### Content Types Identified 1. **Real Estate Properties** (`inmuebles` table) - Property names (`inmueble`) - Descriptions (`descripcion`) - Property types (`tipo_propiedades.titulo`) 2. **Products** (`productos` table) - Sections (`seccion`) - Captions (`epigrafe`) - Descriptions (`descripcion`) 3. **Categories** (`categories` table) - Names (`name`) - Sections (`section`) 4. **Testimonials** (`testimonios` table) - Testimonial content (`testimonio`) - Author names (`autor`) 5. **Static Content** - Website pages and sections - Form labels and messages - Navigation elements ### Database Structure Analysis ```sql -- Key translatable tables inmuebles: inmueble, descripcion productos: seccion, epigrafe, descripcion categories: name, section testimonios: testimonio, autor tipo_propiedades: titulo departamentos: name ciudades: name ``` --- ## Implementation Strategy ### Architecture Overview The implementation follows a **Domain-Driven Design (DDD)** approach with **SOLID principles** and **Repository pattern**: 1. **Translation Domain Services**: Core business logic for translation operations 2. **Repository Pattern**: Data access abstraction with caching layers 3. **Event-Driven Architecture**: Asynchronous translation processing 4. **Strategy Pattern**: Multiple translation providers (Google, DeepL, fallback) 5. **Observer Pattern**: Real-time translation updates and notifications 6. **Factory Pattern**: Dynamic translation service creation 7. **Decorator Pattern**: Caching and rate limiting decorators 8. **Command Pattern**: Queued translation operations ### Supported Languages - **Primary**: Spanish (es) - Current default - **Secondary**: English (en) - Current fallback - **Target**: German (de) - European market expansion ### Language-Specific Considerations #### German Language Features - **Compound Words**: German has long compound words that may require special handling - **Formal/Informal Address**: German has formal (Sie) and informal (Du) forms - **Real Estate Terminology**: Specific German real estate terms for properties, contracts, and legal documents - **Cultural Context**: German-speaking markets may have different preferences for property descriptions #### Translation Quality for German - **Google Translate Accuracy**: Generally high for German (85-90% accuracy) - **Real Estate Domain**: May require manual review for technical terms - **Cultural Adaptation**: Property descriptions may need cultural adjustments --- ## Laravel-Native Senior Implementation ### 1. Laravel Package Structure ``` app/ ├── Services/ │ ├── Translation/ │ │ ├── TranslationService.php │ │ ├── GoogleTranslateService.php │ │ ├── DeepLTranslateService.php │ │ └── TranslationCacheService.php ├── Models/ │ ├── Translation.php │ └── TranslationLog.php ├── Jobs/ │ ├── TranslateContentJob.php │ └── BatchTranslateJob.php ├── Events/ │ ├── TranslationRequested.php │ └── TranslationCompleted.php ├── Listeners/ │ ├── LogTranslationRequest.php │ └── UpdateTranslationMetrics.php ├── Http/ │ ├── Controllers/ │ │ └── TranslationController.php │ ├── Requests/ │ │ └── TranslationRequest.php │ └── Middleware/ │ └── TranslationRateLimit.php ├── Console/ │ ├── Commands/ │ │ ├── TranslateContentCommand.php │ │ └── TranslationStatsCommand.php │ └── Kernel.php ├── Providers/ │ └── TranslationServiceProvider.php └── Facades/ └── Translation.php ``` ### 2. Laravel Service Provider Pattern #### Translation Service Provider ```php app->singleton(TranslationService::class, function ($app) { return new TranslationService( $app->make(GoogleTranslateService::class), $app->make(DeepLTranslateService::class), $app->make(TranslationCacheService::class) ); }); $this->app->singleton(GoogleTranslateService::class, function ($app) { return new GoogleTranslateService( config('services.google_translate.api_key'), config('services.google_translate.project_id') ); }); $this->app->singleton(TranslationCacheService::class, function ($app) { return new TranslationCacheService( Cache::store('redis'), config('translation.cache.ttl', 86400) ); }); } public function boot() { $this->publishes([ __DIR__.'/../config/translation.php' => config_path('translation.php'), ], 'translation-config'); $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); } } ``` #### Main Translation Service (Laravel Style) ```php googleService = $googleService; $this->deeplService = $deeplService; $this->cacheService = $cacheService; } public function translate(string $text, string $targetLocale, string $sourceLocale = 'es'): string { if (empty($text) || $sourceLocale === $targetLocale) { return $text; } $cacheKey = $this->getCacheKey($text, $sourceLocale, $targetLocale); return Cache::remember($cacheKey, config('translation.cache.ttl', 86400), function () use ($text, $targetLocale, $sourceLocale) { return $this->performTranslation($text, $targetLocale, $sourceLocale); }); } public function translateModel($model, string $targetLocale, array $fields = null): array { $fields = $fields ?: $this->getTranslatableFields($model); $translations = []; foreach ($fields as $field) { $originalText = $model->getAttribute($field); if (!empty($originalText)) { $translations[$field] = $this->translate( $originalText, $targetLocale, $model->locale ?? 'es' ); } } return $translations; } protected function performTranslation(string $text, string $targetLocale, string $sourceLocale): string { $providers = [ 'google' => $this->googleService, 'deepl' => $this->deeplService, ]; foreach ($providers as $providerName => $provider) { try { if (!$provider->isAvailable()) { continue; } $result = $provider->translate($text, $targetLocale, $sourceLocale); // Log successful translation $this->logTranslation($text, $result, $providerName, $sourceLocale, $targetLocale); return $result; } catch (\Exception $e) { Log::warning("Translation failed with {$providerName}: {$e->getMessage()}"); continue; } } // Fallback to original text Log::error('All translation providers failed', [ 'text' => $text, 'source' => $sourceLocale, 'target' => $targetLocale ]); return $text; } protected function getCacheKey(string $text, string $sourceLocale, string $targetLocale): string { return 'translation:' . md5($text . $sourceLocale . $targetLocale); } protected function getTranslatableFields($model): array { $translatableFields = [ 'App\Models\Estate' => ['inmueble', 'descripcion'], 'App\Models\Product' => ['seccion', 'epigrafe', 'descripcion'], 'App\Models\Category' => ['name', 'section'], 'App\Models\Testimonial' => ['testimonio', 'autor'], 'App\Models\PropertyType' => ['titulo'], ]; return $translatableFields[get_class($model)] ?? []; } protected function logTranslation(string $original, string $translated, string $provider, string $source, string $target): void { TranslationLog::create([ 'original_text' => $original, 'translated_text' => $translated, 'provider' => $provider, 'source_locale' => $source, 'target_locale' => $target, 'character_count' => strlen($original), ]); } } ``` ### 3. Laravel Eloquent Models #### Translation Model ```php 'float', 'is_auto_translated' => 'boolean', ]; public function translatable(): MorphTo { return $this->morphTo(); } public function scopeForLocale($query, string $locale) { return $query->where('locale', $locale); } public function scopeForField($query, string $field) { return $query->where('field', $field); } public function scopeAutoTranslated($query) { return $query->where('is_auto_translated', true); } } ``` #### Translation Log Model ```php 'integer', 'response_time_ms' => 'integer', 'cost' => 'decimal:6', 'success' => 'boolean', ]; public function scopeSuccessful($query) { return $query->where('success', true); } public function scopeFailed($query) { return $query->where('success', false); } public function scopeForProvider($query, string $provider) { return $query->where('provider', $provider); } public function scopeToday($query) { return $query->whereDate('created_at', Carbon::today()); } public function scopeThisMonth($query) { return $query->whereMonth('created_at', Carbon::now()->month); } } ``` ### 4. Laravel Jobs and Queues #### Translation Job ```php modelType::findOrFail($this->modelId); $originalText = $model->getAttribute($this->field); if (empty($originalText)) { Log::info("Skipping translation for empty field", [ 'model' => $this->modelType, 'id' => $this->modelId, 'field' => $this->field ]); return; } $translatedText = $translationService->translate( $originalText, $this->targetLocale, $this->sourceLocale ); // Save translation $model->translations()->updateOrCreate([ 'locale' => $this->targetLocale, 'field' => $this->field, ], [ 'value' => $translatedText, 'provider' => 'google', // This should come from the service 'is_auto_translated' => true, ]); Log::info("Translation completed", [ 'model' => $this->modelType, 'id' => $this->modelId, 'field' => $this->field, 'locale' => $this->targetLocale ]); } catch (\Exception $e) { Log::error("Translation job failed", [ 'model' => $this->modelType, 'id' => $this->modelId, 'field' => $this->field, 'error' => $e->getMessage() ]); throw $e; } } public function failed(\Throwable $exception): void { Log::critical("Translation job permanently failed", [ 'model' => $this->modelType, 'id' => $this->modelId, 'field' => $this->field, 'error' => $exception->getMessage() ]); } } ``` #### Batch Translation Job ```php batch()->cancelled()) { return; } $modelClass = $this->modelType; $models = $modelClass::whereNull('deleted_at')->get(); foreach ($models as $model) { if ($this->batch()->cancelled()) { break; } $translationService->translateModel($model, $this->targetLocale, $this->fields); } } } ``` ### 5. Laravel Facade #### Translation Facade ```php argument('model'); $locale = $this->argument('locale'); $field = $this->option('field'); $useBatch = $this->option('batch'); if (!class_exists($modelClass)) { $this->error("Model {$modelClass} does not exist."); return 1; } $this->info("Starting translation for {$modelClass} to {$locale}..."); if ($useBatch) { $this->handleBatchTranslation($modelClass, $locale, $field); } else { $this->handleIndividualTranslation($modelClass, $locale, $field); } $this->info('Translation completed successfully!'); return 0; } protected function handleBatchTranslation(string $modelClass, string $locale, ?string $field): void { $batch = Bus::batch([ new BatchTranslateJob($modelClass, $locale, $field ? [$field] : null) ])->name("Translate {$modelClass} to {$locale}") ->onQueue('translations') ->dispatch(); $this->info("Batch job dispatched with ID: {$batch->id}"); } protected function handleIndividualTranslation(string $modelClass, string $locale, ?string $field): void { $models = $modelClass::all(); $bar = $this->output->createProgressBar($models->count()); foreach ($models as $model) { $fields = $field ? [$field] : $this->getTranslatableFields($modelClass); foreach ($fields as $fieldName) { TranslateContentJob::dispatch($modelClass, $model->id, $fieldName, $locale); } $bar->advance(); } $bar->finish(); $this->newLine(); } protected function getTranslatableFields(string $modelClass): array { $fields = [ 'App\\Models\\Estate' => ['inmueble', 'descripcion'], 'App\\Models\\Product' => ['seccion', 'epigrafe', 'descripcion'], 'App\\Models\\Category' => ['name', 'section'], ]; return $fields[$modelClass] ?? []; } } ``` #### Translation Stats Command ```php option('period'); $provider = $this->option('provider'); $query = TranslationLog::query(); // Apply period filter switch ($period) { case 'today': $query->today(); break; case 'week': $query->where('created_at', '>=', Carbon::now()->subWeek()); break; case 'month': $query->thisMonth(); break; } // Apply provider filter if ($provider) { $query->forProvider($provider); } $stats = $this->getStats($query); $this->displayStats($stats, $period, $provider); } protected function getStats($query): array { return [ 'total_requests' => $query->count(), 'successful_requests' => $query->clone()->successful()->count(), 'failed_requests' => $query->clone()->failed()->count(), 'total_characters' => $query->clone()->sum('character_count'), 'total_cost' => $query->clone()->sum('cost'), 'avg_response_time' => $query->clone()->avg('response_time_ms'), 'providers' => $query->clone() ->selectRaw('provider, COUNT(*) as count, SUM(cost) as total_cost') ->groupBy('provider') ->get() ->toArray(), ]; } protected function displayStats(array $stats, string $period, ?string $provider): void { $this->info("Translation Statistics for {$period}" . ($provider ? " (Provider: {$provider})" : "")); $this->line(''); $this->table( ['Metric', 'Value'], [ ['Total Requests', number_format($stats['total_requests'])], ['Successful', number_format($stats['successful_requests'])], ['Failed', number_format($stats['failed_requests'])], ['Total Characters', number_format($stats['total_characters'])], ['Total Cost', '$' . number_format($stats['total_cost'], 4)], ['Avg Response Time', number_format($stats['avg_response_time'], 2) . 'ms'], ] ); if (!empty($stats['providers'])) { $this->line(''); $this->info('Provider Breakdown:'); $this->table( ['Provider', 'Requests', 'Cost'], collect($stats['providers'])->map(function ($provider) { return [ $provider['provider'], number_format($provider['count']), '$' . number_format($provider['total_cost'], 4) ]; })->toArray() ); } } } ``` ### 7. Laravel Controllers and API #### Translation Controller ```php translationService = $translationService; } public function translate(TranslationRequest $request): JsonResponse { $startTime = microtime(true); try { $translatedText = $this->translationService->translate( $request->text, $request->target_locale, $request->source_locale ); $responseTime = round((microtime(true) - $startTime) * 1000); return response()->json([ 'translated_text' => $translatedText, 'source_locale' => $request->source_locale, 'target_locale' => $request->target_locale, 'response_time_ms' => $responseTime, 'cached' => false, // This should come from the service ]); } catch (\Exception $e) { return response()->json([ 'error' => 'Translation failed', 'message' => $e->getMessage() ], 500); } } public function translateModel(Request $request): JsonResponse { $request->validate([ 'model_type' => 'required|string', 'model_id' => 'required|integer', 'target_locale' => 'required|string|in:es,en,de', 'fields' => 'array', ]); try { $modelClass = "App\\Models\\" . $request->model_type; $model = $modelClass::findOrFail($request->model_id); $translations = $this->translationService->translateModel( $model, $request->target_locale, $request->fields ); return response()->json([ 'model_type' => $request->model_type, 'model_id' => $request->model_id, 'translations' => $translations, ]); } catch (\Exception $e) { return response()->json([ 'error' => 'Model translation failed', 'message' => $e->getMessage() ], 500); } } public function stats(Request $request): JsonResponse { $period = $request->get('period', 'today'); $provider = $request->get('provider'); // Implementation similar to the command $query = \App\Models\TranslationLog::query(); switch ($period) { case 'today': $query->today(); break; case 'week': $query->where('created_at', '>=', now()->subWeek()); break; case 'month': $query->thisMonth(); break; } if ($provider) { $query->forProvider($provider); } $stats = [ 'total_requests' => $query->count(), 'successful_requests' => $query->clone()->successful()->count(), 'failed_requests' => $query->clone()->failed()->count(), 'total_characters' => $query->clone()->sum('character_count'), 'total_cost' => $query->clone()->sum('cost'), ]; return response()->json($stats); } } ``` ### 5. Queue-Based Processing #### Translation Job with Retry Logic ```php translate($this->request); // Update model with translation $model = $this->modelType::find($this->modelId); $model->updateTranslation($this->request->getTargetLocale(), $response->getText()); // Fire completion event event(new TranslationCompleted($this->request, $response, $response->getProvider())); } catch (\Exception $e) { Log::error("Translation job failed", [ 'model' => $this->modelType, 'id' => $this->modelId, 'error' => $e->getMessage() ]); throw $e; // Will trigger retry } } public function failed(\Throwable $exception): void { Log::critical("Translation job permanently failed", [ 'model' => $this->modelType, 'id' => $this->modelId, 'error' => $exception->getMessage() ]); } } ``` ### 8. Laravel Translatable Trait #### Laravel-Native Translatable Trait ```php morphMany(Translation::class, 'translatable'); } public function getTranslatedAttribute(string $field, ?string $locale = null): string { $locale = $locale ?: App::getLocale(); if ($locale === $this->getOriginalLocale()) { return $this->getAttribute($field); } $cacheKey = $this->getTranslationCacheKey($field, $locale); return Cache::remember($cacheKey, config('translation.cache.ttl', 86400), function () use ($field, $locale) { $translation = $this->translations() ->forLocale($locale) ->forField($field) ->first(); if ($translation) { return $translation->value; } // Auto-translate if enabled and not already queued if (config('translation.auto_translate.enabled', false) && !$this->isTranslationQueued($field, $locale)) { $this->queueTranslation($field, $locale); } return $this->getAttribute($field); }); } public function queueTranslation(string $field, string $locale): void { TranslateContentJob::dispatch( static::class, $this->id, $field, $locale, $this->getOriginalLocale() ); } public function updateTranslation(string $locale, string $field, string $value, ?string $provider = null): void { $this->translations()->updateOrCreate([ 'locale' => $locale, 'field' => $field, ], [ 'value' => $value, 'provider' => $provider, 'is_auto_translated' => false, ]); $this->clearTranslationCache($field, $locale); } public function getTranslatedFields(string $locale): array { return $this->translations() ->forLocale($locale) ->pluck('value', 'field') ->toArray(); } public function hasTranslation(string $field, string $locale): bool { return $this->translations() ->forLocale($locale) ->forField($field) ->exists(); } public function getAvailableLocales(): array { return $this->translations() ->distinct() ->pluck('locale') ->toArray(); } protected function getOriginalLocale(): string { return $this->locale ?? config('app.locale'); } protected function getTranslationCacheKey(string $field, string $locale): string { return "translation:{$this->getMorphClass()}:{$this->id}:{$field}:{$locale}"; } protected function clearTranslationCache(string $field, string $locale): void { Cache::forget($this->getTranslationCacheKey($field, $locale)); } protected function isTranslationQueued(string $field, string $locale): bool { // Check if translation is already queued (implement with Redis or database) return Cache::has("queued_translation:{$this->getMorphClass()}:{$this->id}:{$field}:{$locale}"); } // Model events for auto-translation protected static function bootTranslatable() { static::created(function ($model) { if (config('translation.auto_translate.on_create', false)) { $model->queueAllTranslations(); } }); static::updated(function ($model) { if (config('translation.auto_translate.on_update', false)) { $model->queueAllTranslations(); } }); } protected function queueAllTranslations(): void { $translatableFields = $this->getTranslatableFields(); $supportedLocales = config('translation.supported_locales', ['en', 'de']); foreach ($translatableFields as $field) { foreach ($supportedLocales as $locale) { if ($locale !== $this->getOriginalLocale()) { $this->queueTranslation($field, $locale); } } } } protected function getTranslatableFields(): array { $translatableFields = [ 'App\Models\Estate' => ['inmueble', 'descripcion'], 'App\Models\Product' => ['seccion', 'epigrafe', 'descripcion'], 'App\Models\Category' => ['name', 'section'], 'App\Models\Testimonial' => ['testimonio', 'autor'], 'App\Models\PropertyType' => ['titulo'], ]; return $translatableFields[static::class] ?? []; } } ``` ### 9. Laravel Model Integration #### Update Existing Models ```php getTranslatedAttribute('inmueble', 'en'); } public function getInmuebleDeAttribute(): string { return $this->getTranslatedAttribute('inmueble', 'de'); } public function getDescripcionEnAttribute(): string { return $this->getTranslatedAttribute('descripcion', 'en'); } public function getDescripcionDeAttribute(): string { return $this->getTranslatedAttribute('descripcion', 'de'); } } ``` #### Blade View Usage ```php {{-- resources/views/estates/show.blade.php --}}

{{ $estate->getTranslatedAttribute('inmueble') }}

{{ $estate->getTranslatedAttribute('descripcion') }}

{{-- Or use accessors --}}

{{ $estate->inmueble_en }}

{{ $estate->descripcion_en }}

``` ### 7. Monitoring and Metrics #### Translation Metrics Service ```php format('Y-m-d'); // Daily metrics Redis::hincrby("translation:daily:{$date}", "{$provider}:count", 1); Redis::hincrby("translation:daily:{$date}", "{$provider}:characters", $characterCount); // Cost tracking $cost = $this->calculateCost($provider, $characterCount); Redis::hincrbyfloat("translation:daily:{$date}", "{$provider}:cost", $cost); // Performance metrics Redis::lpush("translation:performance:{$provider}", time()); Redis::ltrim("translation:performance:{$provider}", 0, 999); // Keep last 1000 } public function recordCacheHit(): void { $date = now()->format('Y-m-d'); Redis::hincrby("translation:daily:{$date}", "cache:hits", 1); } public function getDailyStats(string $date = null): array { $date = $date ?? now()->format('Y-m-d'); return Redis::hgetall("translation:daily:{$date}"); } private function calculateCost(string $provider, int $characterCount): float { $costs = [ 'google' => 0.00002, // $20 per 1M characters 'deepl' => 0.000025, // $25 per 1M characters ]; return ($costs[$provider] ?? 0) * $characterCount; } } ``` ### 8. Configuration Management #### Advanced Translation Configuration ```php env('DEFAULT_LOCALE', 'es'), 'supported_locales' => explode(',', env('SUPPORTED_LOCALES', 'es,en,de')), // Provider configuration 'providers' => [ 'google' => [ 'enabled' => env('GOOGLE_TRANSLATE_ENABLED', true), 'api_key' => env('GOOGLE_TRANSLATE_API_KEY'), 'project_id' => env('GOOGLE_TRANSLATE_PROJECT_ID'), 'timeout' => env('GOOGLE_TRANSLATE_TIMEOUT', 30), 'retry_attempts' => env('GOOGLE_TRANSLATE_RETRIES', 3), ], 'deepl' => [ 'enabled' => env('DEEPL_TRANSLATE_ENABLED', false), 'api_key' => env('DEEPL_TRANSLATE_API_KEY'), 'timeout' => env('DEEPL_TRANSLATE_TIMEOUT', 30), ], ], // Caching configuration 'cache' => [ 'enabled' => env('TRANSLATION_CACHE_ENABLED', true), 'ttl' => env('TRANSLATION_CACHE_TTL', 86400), 'prefix' => env('TRANSLATION_CACHE_PREFIX', 'translation'), 'layers' => ['redis', 'database', 'file'], ], // Queue configuration 'queue' => [ 'enabled' => env('TRANSLATION_QUEUE_ENABLED', true), 'connection' => env('TRANSLATION_QUEUE_CONNECTION', 'redis'), 'queue' => env('TRANSLATION_QUEUE_NAME', 'translations'), 'batch_size' => env('TRANSLATION_BATCH_SIZE', 10), ], // Auto-translation settings 'auto_translate' => [ 'enabled' => env('AUTO_TRANSLATE_ENABLED', false), 'on_create' => env('AUTO_TRANSLATE_ON_CREATE', false), 'on_update' => env('AUTO_TRANSLATE_ON_UPDATE', false), 'min_confidence' => env('AUTO_TRANSLATE_MIN_CONFIDENCE', 0.8), ], // Rate limiting 'rate_limiting' => [ 'enabled' => env('TRANSLATION_RATE_LIMITING', true), 'requests_per_minute' => env('TRANSLATION_RPM', 100), 'burst_limit' => env('TRANSLATION_BURST_LIMIT', 20), ], ]; ``` --- ## Phase-by-Phase Implementation ### Phase 1: Foundation Setup (Week 1) #### 1.1 Package Installation ```bash # Google Translate API package composer require google/cloud-translate # Alternative lightweight option composer require stichoza/google-translate-php # Laravel translation management (Laravel 5.4 compatible) composer require dimsav/laravel-translatable ``` #### 1.2 Environment Configuration Add to `.env`: ```env # Google Translate Configuration GOOGLE_TRANSLATE_API_KEY=your_api_key_here GOOGLE_TRANSLATE_PROJECT_ID=your_project_id_here # Translation Settings DEFAULT_LOCALE=es SUPPORTED_LOCALES=es,en,de CACHE_TRANSLATIONS=true TRANSLATION_CACHE_TTL=86400 AUTO_TRANSLATE=false ``` #### 1.3 Service Configuration Add to `config/services.php`: ```php 'google_translate' => [ 'api_key' => env('GOOGLE_TRANSLATE_API_KEY'), 'project_id' => env('GOOGLE_TRANSLATE_PROJECT_ID'), ], ``` ### Phase 2: Database Structure Enhancement (Week 2) #### 2.1 Translation Storage Table ```php // Migration: create_translations_table.php Schema::create('translations', function (Blueprint $table) { $table->increments('id'); $table->string('translatable_type'); $table->unsignedInteger('translatable_id'); $table->string('locale', 5); $table->string('field'); $table->text('value'); $table->timestamps(); $table->index(['translatable_type', 'translatable_id', 'locale', 'field']); $table->unique(['translatable_type', 'translatable_id', 'locale', 'field']); }); ``` #### 2.2 Locale Support for Existing Tables ```php // Add locale columns to existing tables Schema::table('inmuebles', function (Blueprint $table) { $table->string('locale', 5)->default('es')->after('id'); }); Schema::table('productos', function (Blueprint $table) { $table->string('locale', 5)->default('es')->after('id'); }); Schema::table('categories', function (Blueprint $table) { $table->string('locale', 5)->default('es')->after('id'); }); ``` ### Phase 3: Service Layer Implementation (Week 3) #### 3.1 Google Translate Service ```php translate = new TranslateClient([ 'key' => config('services.google_translate.api_key'), 'projectId' => config('services.google_translate.project_id') ]); } public function translate($text, $targetLanguage, $sourceLanguage = 'es') { if (empty($text) || $sourceLanguage === $targetLanguage) { return $text; } $cacheKey = $this->cachePrefix . md5($text . $sourceLanguage . $targetLanguage); return Cache::remember($cacheKey, $this->cacheTtl, function () use ($text, $targetLanguage, $sourceLanguage) { try { $result = $this->translate->translate($text, [ 'target' => $targetLanguage, 'source' => $sourceLanguage, 'format' => 'html' ]); return $result['text']; } catch (\Exception $e) { Log::error('Google Translate Error: ' . $e->getMessage()); return $text; // Return original text on error } }); } public function translateBatch(array $texts, $targetLanguage, $sourceLanguage = 'es') { $results = []; foreach ($texts as $key => $text) { $results[$key] = $this->translate($text, $targetLanguage, $sourceLanguage); } return $results; } } ``` #### 3.2 Translation Manager Service ```php googleTranslate = $googleTranslate; } public function translateModel(Model $model, $targetLocale, $fields = null) { $fields = $fields ?: $this->getTranslatableFields($model); $translations = []; foreach ($fields as $field) { $originalText = $model->getAttribute($field); if (!empty($originalText)) { $translatedText = $this->googleTranslate->translate( $originalText, $targetLocale, $model->locale ?? 'es' ); $translations[$field] = $translatedText; } } return $translations; } public function saveTranslations(Model $model, $locale, array $translations) { foreach ($translations as $field => $value) { Translation::updateOrCreate([ 'translatable_type' => get_class($model), 'translatable_id' => $model->id, 'locale' => $locale, 'field' => $field ], [ 'value' => $value ]); } } protected function getTranslatableFields(Model $model) { $translatableFields = [ 'App\Models\Estate' => ['inmueble', 'descripcion'], 'App\Models\Product' => ['seccion', 'epigrafe', 'descripcion'], 'App\Models\Category' => ['name', 'section'], 'App\Models\Testimonial' => ['testimonio', 'autor'], 'App\Models\PropertyType' => ['titulo'], ]; return $translatableFields[get_class($model)] ?? []; } } ``` ### Phase 4: Model Enhancements (Week 4) #### 4.1 Translation Model ```php morphTo(); } } ``` #### 4.2 Translatable Trait ```php morphMany(Translation::class, 'translatable'); } public function getTranslatedAttribute($field, $locale = null) { $locale = $locale ?: app()->getLocale(); if ($locale === $this->locale) { return $this->getAttribute($field); } $translation = $this->translations() ->where('locale', $locale) ->where('field', $field) ->first(); return $translation ? $translation->value : $this->getAttribute($field); } public function translateTo($locale, $fields = null) { $translationManager = app(TranslationManager::class); $translations = $translationManager->translateModel($this, $locale, $fields); $translationManager->saveTranslations($this, $locale, $translations); return $translations; } } ``` #### 4.3 Update Existing Models ```php // Add to Estate.php, Product.php, Category.php, etc. use App\Traits\Translatable; class Estate extends Model { use Translatable; // ... existing code } ``` ### Phase 5: Frontend Implementation (Week 5) #### 5.1 Language Switcher Component ```php {{-- resources/views/components/language-switcher.blade.php --}}
``` #### 5.2 Locale Middleware ```php get('locale') ?: Session::get('locale') ?: $request->getPreferredLanguage(['es', 'en', 'de']) ?: config('app.locale'); if (in_array($locale, ['es', 'en', 'de'])) { App::setLocale($locale); Session::put('locale', $locale); } return $next($request); } } ``` #### 5.3 Update Views for Translation ```php {{-- Example: estate show view --}}

{{ $estate->getTranslatedAttribute('inmueble') }}

{{ $estate->getTranslatedAttribute('descripcion') }}

``` ### Phase 6: Admin Tools (Week 6) #### 6.1 Translation Commands ```php argument('model'); $locale = $this->argument('locale'); $field = $this->option('field'); $models = $modelClass::all(); $bar = $this->output->createProgressBar($models->count()); foreach ($models as $model) { $translationManager->translateModel($model, $locale, $field ? [$field] : null); $bar->advance(); } $bar->finish(); $this->info("\nTranslation completed for {$models->count()} records."); } } ``` #### 6.2 Usage Examples ```bash # Translate all estates to English php artisan translate:content Estate en # Translate specific field php artisan translate:content Estate en --field=descripcion # Translate all products to German php artisan translate:content Product de ``` ### Phase 7: Configuration and Testing (Week 7) #### 7.1 Translation Configuration ```php env('DEFAULT_LOCALE', 'es'), 'supported_locales' => explode(',', env('SUPPORTED_LOCALES', 'es,en,de')), 'cache_translations' => env('CACHE_TRANSLATIONS', true), 'cache_ttl' => env('TRANSLATION_CACHE_TTL', 86400), 'auto_translate' => env('AUTO_TRANSLATE', false), 'translatable_models' => [ 'App\Models\Estate' => ['inmueble', 'descripcion'], 'App\Models\Product' => ['seccion', 'epigrafe', 'descripcion'], 'App\Models\Category' => ['name', 'section'], 'App\Models\Testimonial' => ['testimonio', 'autor'], 'App\Models\PropertyType' => ['titulo'], ], ]; ``` #### 7.2 Register Middleware ```php // app/Http/Kernel.php protected $middleware = [ // ... existing middleware \App\Http\Middleware\SetLocale::class, ]; ``` --- ## Testing Strategy ### 1. Unit Testing ```php translate($request); // Second call - should hit cache $response2 = $orchestrator->translate($request); $this->assertEquals($response1->getText(), $response2->getText()); } public function test_provider_fallback() { // Mock primary provider to fail $this->mock(GoogleTranslateProvider::class, function ($mock) { $mock->shouldReceive('isAvailable')->andReturn(false); }); $orchestrator = app(TranslationOrchestrator::class); $request = new TranslationRequest('Hello', 'es', 'en'); $response = $orchestrator->translate($request); $this->assertInstanceOf(TranslationResponse::class, $response); } } ``` ### 2. Integration Testing ```php create([ 'inmueble' => 'Casa en Asunción', 'descripcion' => 'Hermosa casa con jardín' ]); // Trigger translation $estate->queueTranslation('inmueble', 'en'); // Process queue $this->artisan('queue:work', ['--once' => true]); // Verify translation $this->assertEquals('House in Asunción', $estate->getTranslatedAttribute('inmueble', 'en')); } } ``` ### 3. Performance Testing ```php "translated_text_{$i}"); } // Test cache retrieval $cacheStart = microtime(true); for ($i = 0; $i < 100; $i++) { Cache::get("test_key_{$i}"); } $cacheTime = microtime(true) - $cacheStart; $this->assertLessThan(0.1, $cacheTime); // Should be under 100ms } } ``` ### 4. API Testing ```php postJson('/api/translate', [ 'text' => 'Casa en venta', 'target_locale' => 'en', 'source_locale' => 'es' ]); $response->assertStatus(200) ->assertJsonStructure([ 'translated_text', 'provider', 'confidence', 'cached' ]); } } ``` ## Deployment and DevOps ### 1. Docker Configuration ```dockerfile # Dockerfile.translation FROM php:8.1-fpm # Install Redis extension RUN pecl install redis && docker-php-ext-enable redis # Install Google Cloud SDK RUN curl https://sdk.cloud.google.com | bash ENV PATH $PATH:/root/google-cloud-sdk/bin # Copy application COPY . /var/www/html WORKDIR /var/www/html # Install dependencies RUN composer install --no-dev --optimize-autoloader # Set permissions RUN chown -R www-data:www-data /var/www/html/storage ``` ### 2. Kubernetes Deployment ```yaml # k8s/translation-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: translation-service spec: replicas: 3 selector: matchLabels: app: translation-service template: metadata: labels: app: translation-service spec: containers: - name: translation-service image: cerrocora/translation-service:latest ports: - containerPort: 9000 env: - name: GOOGLE_TRANSLATE_API_KEY valueFrom: secretKeyRef: name: translation-secrets key: google-api-key resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 9000 initialDelaySeconds: 30 periodSeconds: 10 ``` ### 3. Monitoring and Alerting ```yaml # monitoring/translation-alerts.yaml groups: - name: translation.rules rules: - alert: TranslationAPIHighErrorRate expr: rate(translation_api_errors_total[5m]) > 0.1 for: 2m labels: severity: warning annotations: summary: "High translation API error rate" - alert: TranslationCostExceeded expr: translation_daily_cost > 100 for: 0m labels: severity: critical annotations: summary: "Daily translation cost exceeded $100" ``` ### 4. CI/CD Pipeline ```yaml # .github/workflows/translation-deploy.yml name: Translation Service Deploy on: push: branches: [main] paths: ['app/Domains/Translation/**'] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.1' - name: Install dependencies run: composer install - name: Run tests run: | php artisan test --testsuite=Unit php artisan test --testsuite=Feature - name: Run performance tests run: php artisan test --testsuite=Performance deploy: needs: test runs-on: ubuntu-latest steps: - name: Deploy to staging run: | kubectl apply -f k8s/translation-deployment.yaml kubectl rollout status deployment/translation-service ``` ## Security Considerations ### 1. API Key Management ```php $encryptedKey]); // Clear cache Cache::forget("encrypted_api_key_{$provider}"); } } ``` ### 2. Rate Limiting ```php ip(); $limit = config('translation.rate_limiting.requests_per_minute', 100); $current = Redis::incr($key); if ($current === 1) { Redis::expire($key, 60); } if ($current > $limit) { return response()->json(['error' => 'Rate limit exceeded'], 429); } return $next($request); } } ``` ### 3. Input Validation and Sanitization ```php 'required|string|max:5000', 'target_locale' => 'required|string|in:es,en,de', 'source_locale' => 'string|in:es,en,de', ]; } public function prepareForValidation() { $this->merge([ 'text' => strip_tags($this->text), 'source_locale' => $this->source_locale ?? 'es', ]); } } ``` --- ## Technical Implementation Details ### Caching Strategy - **Translation Cache**: 24-hour TTL for translated content - **Model Cache**: Cache translated attributes per model instance - **API Response Cache**: Cache Google Translate API responses ### Error Handling - **API Failures**: Return original text if translation fails - **Rate Limiting**: Implement request throttling for API calls - **Logging**: Comprehensive error logging for debugging ### Performance Optimization - **Batch Translation**: Process multiple texts in single API calls - **Lazy Loading**: Load translations only when needed - **Database Indexing**: Optimize translation table queries ### Security Considerations - **API Key Protection**: Store keys in environment variables - **Input Validation**: Sanitize text before translation - **Rate Limiting**: Prevent API abuse --- ## Cost Analysis ### Google Translate API Pricing - **Standard Pricing**: $20 per 1M characters - **Estimated Monthly Usage**: 2-10M characters - **Estimated Monthly Cost**: $40-200 ### Cost Optimization Strategies 1. **Caching**: 80-90% reduction in API calls 2. **Batch Processing**: Reduce API overhead 3. **Selective Translation**: Only translate when needed 4. **Content Filtering**: Skip empty or duplicate content ### ROI Calculation - **Implementation Cost**: ~40 hours development - **Monthly Operating Cost**: $50-150 - **Potential Revenue Increase**: 15-25% from international traffic - **Break-even Point**: 3-6 months --- ## Benefits and ROI ### Business Benefits 1. **Market Expansion**: Reach German and English-speaking markets 2. **SEO Improvement**: Better search rankings in multiple languages 3. **User Experience**: Native language experience for international users 4. **Competitive Advantage**: First-mover advantage in regional market ### Technical Benefits 1. **Scalability**: Easy to add new languages 2. **Maintainability**: Centralized translation management 3. **Performance**: Optimized caching and batch processing 4. **Flexibility**: Manual override capabilities for translations ### User Experience Benefits 1. **Accessibility**: Content available in user's preferred language 2. **Engagement**: Higher engagement from international visitors 3. **Conversion**: Better conversion rates for non-Spanish speakers 4. **Trust**: Professional appearance with proper localization --- ## Maintenance and Support ### Ongoing Maintenance Tasks 1. **Monthly**: Review translation quality and accuracy 2. **Quarterly**: Update Google Translate API usage and costs 3. **Annually**: Evaluate new language requirements ### Monitoring and Analytics 1. **Translation Usage**: Track which languages are most used 2. **API Performance**: Monitor response times and error rates 3. **User Behavior**: Analyze language switching patterns 4. **Cost Tracking**: Monitor API usage and costs ### Support Procedures 1. **Translation Issues**: Manual review and correction process 2. **API Problems**: Fallback to original content 3. **Performance Issues**: Cache optimization and debugging 4. **New Content**: Automatic translation for new content ### Documentation Updates 1. **API Changes**: Update when Google Translate API changes 2. **New Features**: Document new translation features 3. **Troubleshooting**: Maintain troubleshooting guides 4. **Best Practices**: Update implementation guidelines --- ## Conclusion This comprehensive Google Translate implementation strategy provides a robust, scalable solution for multi-language support in the Cerro Corá real estate website. The phased approach ensures minimal disruption to existing functionality while providing maximum benefit for international users. The implementation is designed to be cost-effective, maintainable, and user-friendly, with proper error handling and performance optimization. The estimated timeline of 7 weeks allows for thorough testing and refinement of each component. ### Next Steps 1. **Approval**: Review and approve the implementation strategy 2. **Resource Allocation**: Assign development team and timeline 3. **Google Cloud Setup**: Configure Google Translate API access 4. **Development**: Begin Phase 1 implementation 5. **Testing**: Comprehensive testing of each phase 6. **Deployment**: Gradual rollout with monitoring ### Success Metrics - **Translation Accuracy**: >90% accuracy for German real estate terminology - **Performance Impact**: <100ms additional load time - **User Adoption**: >20% of users switching languages - **Cost Efficiency**: <$150/month operating cost - **SEO Improvement**: 15% increase in international traffic - **German Market Penetration**: Track German-speaking user engagement --- *Document Version: 1.0* *Last Updated: [Current Date]* *Prepared by: Development Team* *Project: Cerro Corá Google Translate Implementation*