├── .env.example ├── .gitignore ├── LICENSE ├── README.md ├── fr.json ├── package-lock.json ├── package.json ├── src ├── converter.ts ├── directory.ts ├── file.ts ├── helper.ts ├── index.ts └── translate.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | AUTH_KEY="YOUR AUTH KEY HERE" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | tools/output 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 AbdelhamidLarachi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-translator-i18next 2 | 3 | [![GitHub license](https://img.shields.io/github/license/AbdelhamidLarachi/React-translator-i18next)](https://github.com/AbdelhamidLarachi/React-translator-i18next/blob/master/LICENSE) ![GitHub license](https://img.shields.io/github/issues/AbdelhamidLarachi/React-translator-i18next) 4 | 5 | ***Open-Source*** project made to ***auto translate*** react apps using ***i18next***, this project should ***extract***, ***translate*** and ***replace*** your components texts, then ***generates*** the Json language file. 6 | 7 | 8 | ## Works as follows : 9 | 10 | - **Extract text from components / props / yup :** extract text from `components with no children`, props like `placeholder`, `label`, `title`..., `snackbars`, and every text in `yup schemas`. 11 | - **Convert JSX / TSX files to i18n hook :** by replacing theses texts with your i18n hook function, adding imports, and initializing hooks ex: from `text` to `{t(text)}`. 12 | - **Translate detected texts :** deepl api was used for translating part, you just need to set your `free api key` on the .env file. 13 | - **Generate JSON language file:** it should generate the final json files for the languages you've selected. 14 | 15 | 16 | ## Installation 17 | 18 | * Set your default project path in ` src/directory.ts`. `Desktop/project` by default ( don't forget to make a project copy ). 19 | * Select your target languages in `src/translate.ts`, by default it's `['en-US', 'fr']` 20 | * Create .env file with `AUTH_KEY` value (deepl api key) 21 | * Run `npm install` then > `npm start` 22 | * Check your `project/langs` folder for json files 23 | * Your JSX / TSX files should be translated now. 24 | 25 | ## Contributing 26 | 27 | The main purpose of this repository is to continue evolving and to help lazy ass developers, we should expect a lot of bugs in this type of projects, so i invite you to contribute by creating a pull request or report issues. 28 | 29 | Thanks for your support. 30 | 31 | ### License 32 | 33 | [MIT licensed](./LICENSE). 34 | -------------------------------------------------------------------------------- /fr.json: -------------------------------------------------------------------------------- 1 | {"translation":{"Andaloussia":"Andalousie","Go_Back":"Aller en arrière","Go_to_Home":"Aller à l'accueil","Not_found":"Non trouvé","Exclusive_products":"Produits exclusifs","Sort_by:":"Trier par:","View:":"Voir:","Short_by":"Courir par","Arrivals":"Arrivées","Total:":"Total:","Additional_Comments":"Commentaires supplémentaires","Note":"Note","Apply_Voucher":"Appliquer le bon d'achat","Shipping_Estimates":"Estimations d'expédition","Shipping_price:":"Prix d'expédition:","Calculate_Shipping":"Calculer les frais d'expédition","Checkout_Now":"Caisser maintenant","Cart":"Cart","Voucher":"Bon d'achat","Wilaya":"Wilaya","Select_Wilaya":"Sélectionnez Wilaya","Checkout":"Checkout","How_Can_We_Help_?":"Comment pouvons-nous vous aider ?","Please_select_a_topic_below_related_to_your_inquiry._If_you_dont_find_what_you_need,_fill_out_our_contact_form.":"Veuillez sélectionner ci-dessous un sujet lié à votre demande. Si vous ne trouvez pas ce dont vous avez besoin, remplissez notre formulaire de contact.","Lorem_ipsum_dolor_sit_amet,_consectetur_adipiscing_?":"Lorem ipsum dolor sit amet, consectetur adipiscing ?","Lorem_ipsum_dolor_sit_amet,_consectetur_adipiscing_elit._Suspendisse_malesuada_lacus_ex,_sit_amet_blandit_leo_lobortis_eget.":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget.","Suspendisse_malesuada_lacus_ex_?":"Suspendisse malesuada lacus ex ?","blandit_leo_lobortis_eget_?":"blandit leo lobortis eget ?","Contact_us_!":"Contactez-nous !","Contact_Us":"Contactez-nous","Fill_in_the_form_with_your_inquiry_and_we_will_get_in_touch_with_you_as_soon_as_possible":"Remplissez le formulaire avec votre demande et nous vous contacterons dans les plus brefs délais","Subject":"Sujet","Submit":"Soumettre","Help":"Aide","Name":"Nom","Phone_Number":"Numéro de téléphone","Home":"Accueil","Mobile_Phones":"Téléphones portables","Optics_/_Watch":"Optique / Montre","Login":"Login.","Recommended_Categories":"Catégories recommandées","Your_order_is_completed!":"Votre commande est terminée!","You_will_be_receiving_confirmation_email_with_order_details.":"Vous recevrez un courriel de confirmation avec les détails de la commande.","Browse_products":"Parcourir les produits","Order_Confirmation":"Confirmation de la commande","Order_ID:":"Identification de la commande:","Created_on:":"Créée le:","Delivered_on:":"Livrée le:","Write_a_Review":"Écrire une critique","Shipping_Address":"Adresse de livraison","Total":"Total","Order_Details":"Détails de la commande","Orders":"Commandes","My_Orders":"Mes commandes","Back_to_Payment_Methods":"Retour aux modes de paiement","Save_Changes":"Enregistrer les modifications","Card_Number":"Numéro de carte","Name_on_Card":"Nom de la carte","Exp._Date":"Date d'exp. Date d'expiration","CVC":"CVC","required":"requis","Add_New_Payment_Method":"Ajouter un nouveau mode de paiement","Payment_Methods":"Modes de paiement","Payment":"Paiement","Loading...":"Chargement...","Sorry,_Product_not_found.":"Désolé, produit non trouvé.","Description":"Description","Explore_our_products":"Explorer nos produits","item_available.":"article disponible.","Searching_for":"Recherche de ","Edit_Profile":"Modifier le profil","SILVER_USER":"SILVER USER","Email":"Email","Phone":"Téléphone","Profile":"Profil","My_Profile":"Mon profil","Reset_Password":"Réinitialiser le mot de passe","Shops":"Shops","Explore_our_brands":"Explorer nos marques","Showing_1-9_of":"Affichage de 1 à 9 sur.","Sign_up":"Inscrivez-vous","Back_to_Support_Ticket":"Retour au ticket d'assistance","Post_message":"Poster un message","Support_Ticket":"Billet d'assistance","Write_your_message_here...":"Écrivez votre message ici...","My_product_is_broken._I_need_refund":"Mon produit est cassé. J'ai besoin d'un remboursement","Website_Problem":"Problème de site web","Urgent":"Urgent","Open":"Open","Store_Terms_of_service":"Conditions de service du magasin","Please_read_the_following_terms_of_service,_and_understand_they_are_a_legally_binding_document._We_encourage_you_to_review_the_contents_carefully_in_their_entirety_and_to_seek_legal_counsel_if_you_have_any_concerns._Please_note_that_your_use_of_our_Website_constitutes_your_acceptance_of_these_terms_of_service_and_your_agreement_to_be_bound_by_them.":"Veuillez lire les conditions de service suivantes et comprendre qu'elles constituent un document juridiquement contraignant. Nous vous encourageons à en examiner attentivement le contenu dans son intégralité et à consulter un conseiller juridique si vous avez des inquiétudes. Veuillez noter que votre utilisation de notre site Web constitue votre acceptation de ces conditions de service et votre accord pour être lié par elles.","Copyrights":"Copyright","Andaloussia_is_an_online_sales_platform_specializing_in_childcare_under_register_number:_14B1007947/16-00.":"Andaloussia est une plateforme de vente en ligne spécialisée dans la garde d'enfants sous le numéro de registre : 14B1007947/16-00.","All_content_present_on_andaloussia-shop.co,_including_styles,_images,_animations,_texts,_databases,_programs…_is_protected_by_copyright._Any_use_of_the_content_requires_the_prior_written_consent_of_Andaloussia.":"Tout le contenu présent sur andaloussia-shop.co, y compris les styles, images, animations, textes, bases de données, programmes... est protégé par le droit d'auteur. Toute utilisation du contenu nécessite le consentement écrit préalable d'Andaloussia.","Terms_of_use":"Conditions d'utilisation","Refund_conditions":"Conditions de remboursement","To_make_a_refund,_please_send_the_product_in_packaging_with_the_delivery_note_within_48_hours,_the_postmark_being_valid,_you_will_receive_your_refund_within_48_hours_of_receipt_of_the_product.":"Pour effectuer un remboursement, veuillez envoyer le produit dans son emballage avec le bon de livraison dans les 48 heures, le cachet de la poste faisant foi, vous recevrez votre remboursement dans les 48 heures suivant la réception du produit.","Privacy_and_data_protection_policy":"Politique de confidentialité et de protection des données","Personal_collected_data":"Données personnelles collectées","Andaloussia-shop.com_collects_the_following_personal_data_from_you:_Your_surname,_first_name,_city,_address,_email_address,_telephone_number,_password,_orders._The_mandatory_nature_of_the_data_collected_is_indicated_by_an_asterisk_or_a_red_triangle,_the_other_data_are_optional.":"Andaloussia-shop.com collecte auprès de vous les données personnelles suivantes : Votre nom, prénom, ville, adresse, adresse email, numéro de téléphone, mot de passe, commandes. Le caractère obligatoire des données collectées est indiqué par un astérisque ou un triangle rouge, les autres données sont facultatives.","Collection_purposes":"Finalités de la collecte","Andaloussia-shop.com_collects_user_data_for_different_purposes:":"Andaloussia-shop.com collecte les données des utilisateurs pour différentes finalités :","Orders:_andaloussia-shop.com_collects_users_personal_data_to_manage_their_product_orders_and_process_any_complaints.":"Commandes : andaloussia-shop.com collecte les données personnelles des utilisateurs pour gérer leurs commandes de produits et traiter leurs éventuelles réclamations.","In_addition,_andaloussia-shop.com_may_analyze_your_data_in_order_to_improve_and_develop_new_services,_identify_trends_in_the_use_of_the_Site_and_determine_the_effectiveness_of_its_promotional_campaigns._In_addition,_this_data_will_be_kept_in_order_to_comply_with_various_legal_and_regulatory_obligations,_in_particular_to_enable_us_to_establish_proof_of_a_right_or_a_contract._andaloussia-shop.com_reserves_the_right_to_use_the_information_collected_for_commercial_purposes.":"En outre, andaloussia-shop.com peut analyser vos données afin d'améliorer et de développer de nouveaux services, d'identifier les tendances d'utilisation du Site et de déterminer l'efficacité de ses campagnes promotionnelles. En outre, ces données seront conservées afin de respecter les différentes obligations légales et réglementaires, notamment pour nous permettre d'établir la preuve d'un droit ou d'un contrat. andaloussia-shop.com se réserve le droit d'utiliser les informations collectées à des fins commerciales.","Newsletters:_andaloussia-shop.com_may_use_your_personal_data_to_send_you_promotional_and_commercial_offers._As_soon_as_you_create_your_account_or_from_the_Newsletter_section,_you_can_manage_the_sending_of_newsletters.":"Newsletters : andaloussia-shop.com pourra utiliser vos données personnelles pour vous adresser des offres promotionnelles et commerciales. Dès la création de votre compte ou à partir de la rubrique Newsletter, vous pouvez gérer l'envoi de newsletters.","Termination":"Résiliation","andaloussia-shop.com_may_automatically_refuse_the_use_of_its_services_to_Users,_without_delay_or_compensation_and_without_prejudice_to_any_compensation,_in_the_event_of_a_breach_by_the_User_of_one_of_his_obligations,_whatever_they_may_be._appear_in_these_general_conditions,_and_in_particular_in_the_following_case,_in_the_event_of_any_act_of_piracy_or_attempted_illicit_use_of_the_information_circulating_on_andaloussia-shop.com_having_as_its_cause_or_origin_the_connection_with_the_post_of_the_User_The_only_rights_and_remedies_available_to_you_exclusively_in_the_event_of_dissatisfaction_with_regard_to_the_site,_the_services_offered_or_any_other_grievance_are_the_termination_and_interruption_of_your_access_to_the_site_or_its_use.":"andaloussia-shop.com pourra refuser de plein droit l'utilisation de ses services aux Utilisateurs, sans délai ni indemnité et sans préjudice de toute indemnisation, en cas de manquement par l'Utilisateur à l'une de ses obligations, quelles qu'elles soient. figurent dans les présentes conditions générales, et notamment dans le cas suivant, en cas d'acte de piratage ou de tentative d'utilisation illicite des informations circulant sur andaloussia-shop.com ayant pour cause ou origine le lien avec le poste de l'Utilisateur. Les seuls droits et recours dont vous disposez exclusivement en cas d'insatisfaction à l'égard du site, des services proposés ou de tout autre grief sont la résiliation et l'interruption de votre accès au site ou de son utilisation.","Terms":"Termes","Add_All_to_Cart":"Ajouter tout au panier","Wishlist":"Liste de souhaits","My_Wish_List":"Ma liste de souhaits","Shop_Now":"Commander maintenant","comments":"commentaires","CONTINUE_READING":"CONTINUER DE LIRE","Deal_Of_The_Day":"Deal Of The Day","Lorem_ipsum_dolor_sit_amet,_consectetur_adipiscing_elit._Quis_lobortis_consequat_eu,_quam_etiam_at_quis_ut_convallis.":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quis lobortis consequat eu, quam etiam at quis ut convallis.","Fresh_Deal_Everyday,_Get_It_Now!":"Fresh Deal Everyday, Get It Now!","BUY_NOW":"BUY NOW","Big_Sale_Upto_60%_Off":"Big Sale Upto 60% Off","Handcrafted_from_genuine_Italian_Leather":"Handcrafted from genuine Italian Leather.","SHOP_NOW":"SHOP NOW","OR":"OR","Select_files":"Sélectionner les fichiers","Kids_&_babies_essentials_and_clothing_online_shopping_at_Andalousia_|_Enjoy_great_deals,_offers_&_fast_delivery_in_Algeria.":"Les essentiels et les vêtements pour enfants et bébés en ligne sur Andalousia | Profitez de bonnes affaires, d'offres et d'une livraison rapide en Algérie.","About_Us":"A propos de nous","Customer_Care":"Service clientèle","Lot_Al_Yasmine_Ext_n°_30_Draria_16000_Algerie":"Lot Al Yasmine Ext n° 30 Draria 16000 Algerie","Email:_andaloussia.info@gmail.com":"Email : andaloussia.info@gmail.com","Browse_Website":"Parcourir le site web","Search_anything...":"Recherche tout...","Gage_Paquette":"Gage Paquette","Admin":"Admin","Settings":"Paramètres","Logout":"Connexion","There_are_no_notifications":"Il n'y a pas de notifications","There_are_no_archives":"Il n'y a pas d'archives","Unread_(2)":"Unread (2)","Archived":"Archived","item":"item","Your_shopping_bag_is_empty._Start_shopping":"Votre panier est vide. Commencez vos achats","View_Cart":"Voir le panier"," ":" ","Categories":"Catégories","Lorem_ipsum_dolor_sit_amet,_consectetur_adipiscing_elit._Auctor_libero_id_et,_in_gravida._Sit_diam_duis_mauris_nulla_cursus._Erat_et_lectus_vel_ut_sollicitudin_elit_at_amet.":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Auctor libero id et, in gravida. Sit diam duis mauris nulla cursus. Erat et lectus vel ut sollicitudin elit at amet.","300ml":"300ml","Remove_from_Cart":"Sortir du panier","Add_to_Cart":"Ajouter au panier","%_OFF":"% OFF","Quick_View":"Visualisation rapide","Add_To_Cart":"Ajouter au panier","Price":"Prix","Quantity":"Quantités","Also_Available_at":"Aussi disponible chez","Frequently_Bought_Together":"Fréquemment achetés ensemble","Save_$500":"Economiser 500$","Add_to_Wish-list":"Ajouter à la liste de souhaits","Products":"Produits","Specification:":"Spécification:","Price_Range":"Plage de prix","Brands":"Marques.","Availability":"Disponibilité","Filter":"Filtre","Reset":"Reset","Ratings":"Cotes","Colors":"Couleurs","Brand:":"Marque:","Barcode:":"Barcode:","Reference:":"Référence:","Rated:":"Note:","Sold_By:":"Vendu par:","Write_a_Review_for_this_product":"Écrire une critique pour ce produit","Your_Rating":"Votre note","Your_Review":"Votre critique","Write_a_review_here...":"Écrire une critique ici...","CATEGORY:":"CATEGORIE:","Related_Products":"Produits connexes","Search":"Recherche","Searching_for...":"Recherche de...","Settings_&_Demos":"Settings & Demos","845_N._Stonybrook_Ave._Tonawanda,_NY_14210,_Denmark":"845 N. Stonybrook Ave. Tonawanda, NY 14210, Danemark","Vous_navez_aucune_notification_pour_le_moment.":"Vous ne navez aucune notification pour le moment.","andaloussia.info@gmail.com":"andaloussia.info@gmail.com","Theme_FAQ"s":"Thème FAQ"s","Need_Help?":"Besoin d'aide ? ","Placed_on:":"Placé le:","01_Jan,_2021":"01 Jan, 2021","Processing":"Processing","Pending":"Pending","Delivered":"Delivered","x":"x","Product_properties:_Black,_L":"Propriétés du produit : Noir, L","Total_Summary":"Résumé total","Subtotal:":"Sous-total:","Shipping_fee:":"Frais de port:","Discount:":"Remise:","Paid_by_Credit/Debit_Card":"Paiement par carte de crédit/débit","Add_Product":"Ajouter un produit","Type_product_name":"Type de nom de produit","Order_Status":"État de la commande","Customers_Note":"Note des clients.","Electronics":"Electronique","Fashion":"Mode","Save_product":"Enregistrer le produit","Category":"Catégorie","Select_Category":"Sélectionner la catégorie","Stock":"Stock","Tags":"Etiquettes","Regular_Price":"Prix normal","Sale_Price":"Prix de vente","Package":"Package","Edit":"Editer","Delete":"Supprimer","Catalog":"Catalogue","Brand":"Marque","Supplier":"Fournisseur","Created_on":"Créé le","No_arrivals_at_this_moment.":"Aucun arrivage pour le moment.","View_products":"Voir les produits","User_Informations":"Informations utilisateur","Back_to_Cart":"Retour au panier","Proceed_to_Payment":"Procéder au paiement","City":"Ville","Address":"Adresse","first_name_must_be_at_least_3_characters":"Le prénom doit comporter au moins 3 caractères","last_name_must_be_at_least_3_characters":"Le nom de famille doit comporter au moins 3 caractères","city_must_be_at_least_3_characters":"La ville doit comporter au moins 3 caractères","address_must_be_at_least_10_characters":"L'adresse doit comporter au moins 10 caractères","Please_choose_a_wilaya.":"Veuillez choisir une wilaya.","Wilaya_is_required":"La wilaya est obligatoire","invalid_email":"Mail invalide","Phone_number_is_not_valid":"Le numéro de téléphone n'est pas valide","Changes_applied.":"Modifications appliquées.","Redirecting_to_payment...":"Redirection vers le paiement...","Delivery_Address":"Adresse de livraison","Enter_Card_Information":"Entrer les informations de la carte","Saved_Cards":"Cartes enregistrées","I_have_a_voucher":"J'ai un bon","Apply":"Demander","Place_Order":"Passer la commande","Delivery_Date_and_Time":"Date et heure de livraison.","Delivery_Date":"Date de livraison","Delivery_Time":"Heure de livraison","Payment_Details":"Détails de paiement","Enter_Your_Name":"Entrez votre nom","Enter_Your_Card_Number":"Entrez votre numéro de carte","Expire_Card_Month":"Mois d'expiration de la carte","Expire_Card_Year":"Année d'expiration de la carte","CVC/CVV":"CVC/CVV","Save_this_card":"Enregistrez cette carte","Enter_voucher_code_here":"Entrez le code de bon ici","Shipping:":"Envoi :","Tax:":"Taxe:","Your_order":"Votre commande","Edit_Address_Information":"Modifier les informations d'adresse","Save":"Enregistrer","Address_line_1":"Ligne d'adresse 1","Address_line_2":"Ligne d'adresse 2","Enter_Your_Phone":"Entrer votre téléphone","Add_New_Address":"Ajouter une nouvelle adresse","Add_New_Address_Information":"Ajouter de nouvelles informations d'adresse.","Street_line_1":"Street line 1","State":"State","Zip":"Zip","Country":"Country","Analytics":"Analytics","Yearly":"Yearly","Monthly":"Monthly","Weekily":"Weekily","Recent_Purchases":"Recent Purchases","All_Orders":"All Orders","Weekly_Sales":"Weekly Sales","Product_Share":"Product Share","Total_Order":"Total Order","Market_Share":"Market Share","Stock_Out_Products":"Stock Out Products","All_Products":"All Products","Good_Morning,_Maruf!":"Good Morning, Maruf !","Heres_what_happening_with_your_store_today!":"Voici ce qui se passe avec votre magasin aujourd'hui!","Todays_Visit":"Visites de la semaine","Todays_total_sales":"Ventes totales de la semaine.","Features":"Caractéristiques","Demos":"Démos","Documentation":"Documentation","Purchase_Now":"Achats immédiats","Technologies":"Technologies","Build_your_online_store_with":"Construisez votre boutique en ligne avec","Andalousia":"Andalousia","SSR":"SSR","Rest_API":"Rest API","Multi_vendor_Support":"Support multi-vendeurs","What's_inside":"Qu'est-ce qui se passe ?s inside","View_Demos":"View Demos","I_need_a_Server_&_Database":"I need a Server & Database","Powerful_Features":"Powerful Features","Server_side_rendered":"Server side rendered","Demos_&_Pages":"Demos & Pages","All":"All","Homepages":"Homepages","Shop":"Shop","User_Dashboard":"User Dashboard","Admin_Dashboard":"Tableau de bord de l'administrateur","Technologies_Used":"Technologies utilisées","you_Ecommerce_Store?":"votre boutique en ligne","Regular":"Régulières","are_not_charged":"ne sont pas facturées","for.":"pour.","Extended_License":"Licence étendue","What_You_Get?":"Ce que vous obtenez","More_For_You":"Plus pour vous","Big_Discounts":"De grosses remises","Popular_products":"Produits populaires","Top_Ratings":"Top Ratings","Featured_Brands":"Marques en vedette","New_Arrivals":"Nouveaux arrivages","View_All_Brands":"Voir toutes les marques","Cars":"Voitures","ID":"ID","Status":"Statut","Purchased_on":"Acheté le","You_have_no_orders.":"Vous n'avez aucune commande.","Pay_with_Credit_Card":"Pay with Credit Card","Cash_On_Delivery":"Cash On Delivery","Back_to_checkout_details":"Back to checkout details","Finish_&_Review":"Finish & Review","Exp_Date":"Exp Date","MM/YY":"MM/YY",").max(3).min(3),_//_shipping_zip:_yup.string().required(":").max(3).min(3), // shipping_zip : yup.string().required(","Your_order_has_been_created.":"Votre commande a été créée.","Change_Password":"Changer le mot de passe","Current_password":"Mot de passe actuel","New_password":"Nouveau mot de passe","Confirm_password":"Confirmer le mot de passe","New_Password":"Nouveau mot de passe",").min(6).max(40).required().notOneOf([yup.ref(":").min(6).max(40).required().notOneOf([yup.ref(",")],":")],","You_can":"Vous ne pouvez","t_choose_the_same_password":"pas choisir le même mot de passe","Confirm_Password":"Confirmer le mot de passe","Password_do_not_match":"Mot de passe ne correspond pas","Password_updated!":"Mot de passe mis à jour !","Back_to_Profile":"Retour au profil","First_Name":"Prénom","Last_Name":"Nom de famille","Nothing_to_update.":"Rien à mettre à jour.","Profile_updated!":"Profil mis à jour!","Welcome_To_Andalousia":"Bienvenue à Andalousia","Log_in_with_phone_&_password":"Connexion avec téléphone et mot de passe","Password":"Mot de passe","Password_is_required.":"Le mot de passe est requis.",").required(":").required(","Phone_number_is_required.":"Le numéro de téléphone est requis.","Logged_in.":"Connexion.","Forgot_your_password_?":"Mot de passe oublié ?","Please_enter_the_phone_number_for_your_account._A_verification_code_will_be_sent_to_you._Once_you_have_received_the_verification_code._You_wil_be_able_to_choose_a_new_password_for_your_account.":"Veuillez saisir le numéro de téléphone de votre compte. Un code de vérification vous sera envoyé. Une fois que vous aurez reçu le code de vérification. Vous pourrez choisir un nouveau mot de passe pour votre compte.","OTP_Verification":"OTP Verification","Enter_the_six_digits_you_received_via_SMS":"Entrez les six chiffres que vous avez reçus par SMS","Enter_your_new_password":"Entrez votre nouveau mot de passe","Success!":"Success!","Your_password_has_been_successfully_reset._Now_you_can_login_using_your_new_password.":"Votre mot de passe a été réinitialisé avec succès. Vous pouvez maintenant vous connecter en utilisant votre nouveau mot de passe.","Confirm_your_password":"Confirmez votre mot de passe.","password_must_be_at_least_6_characters":"Le mot de passe doit comporter au moins 6 caractères","Code_sent_to_your_phone_number.":"Code envoyé à votre numéro de téléphone.","Success.":"Succès.","Password_has_been_reset.":"Le mot de passe a été réinitialisé.","Redirecting...":"Redirection...","Create_Your_Account":"Créer votre compte","Please_fill_all_fields_to_continue":"Veuillez remplir tous les champs pour continuer","Terms_&_Condtion":"Termes et conditions","Create_Account":"Créer un compte","Firstname":"Prénom","John":"John","Lastname":"Nom de famille","Snow":"Snow","Email_(Optional)":"Email (facultatif)","andalousia@mail.com":"andalousia@mail.com","Alger_centre":"Alger centre","Rue_laarbi_tbessi_BT_32,_Alger_centre":"Rue laarbi tbessi BT 32, Alger centre","Retype_Password":"Taper le mot de passe","Firstname_is_required":"Prénom est obligatoire","Lastname_is_required":"Nom de famille est obligatoire.","City_is_required":"La ville est requise","Address_is_required":"L'adresse est requise","Password_is_required":"Le mot de passe est requis","agreement":"Accord","You_have_to_agree_with_our_Terms_and_Conditions!":"Vous devez accepter nos conditions générales!","Signed_up.":"Signé.","or":"ou","Continue_with_Facebook":"Continuer avec Facebook","Continue_with_Google":"Continuer avec Google","Dont_have_account?":"N'avez pas de compte ?","Reset_It":"Réinitialiser","Drag_and_Drop_slide_image_here":"Glissez et déposez l'image du diaporama ici","Second_Column_Content":"Contenu de la deuxième colonne","Add_Item":"Ajouter un article","Third_Column_Content":"Contenu de la troisième colonne","Four_Column_Content":"Contenu de la quatrième colonne","Drag_&_Drop_Footer_Logo":"Glissez et déposez le logo du pied de page","Footer_Description":"Description du pied de page","Heading_Name":"Nom de la rubrique","Link":"Lien.","Heading":"Heading","Drag_&_Drop_Site_Logo":"Drag & Drop Site Logo","Site_Name":"Site Name","Site_Description":"Site Description","Site_Banner_Text":"Site Banner Text","Drag_&_Drop_Site_Banner_Image":"Drag & Drop Site Banner Image","site_name_is_required":"site name is required","site_description_is_required":"site description is required","site_banner_text_required":"site. texte de bannière requis","Shipping_and_Vat":"Transport et TVA","Shipping_Charge":"Frais de port","VAT_(%)":"TVA (%)","Social_Links":"Liens sociaux","App_Links":"Liens d'application","Facebook":"Facebook","Twitter":"Twitter","Instagram":"Instagram","Youtube":"Youtube","Play_Store":"Play Store","App_Store":"App Store.","Top_Bar_Left_Content":"Barre supérieure gauche Contenu","Top_Bar_Right":"Barre supérieure droite","email@example.com":"email@example.com"}} -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "author": "Abdelhamid Larachi", 7 | "packages": { 8 | "": { 9 | "name": "node", 10 | "version": "1.0.0", 11 | "license": "ISC", 12 | "dependencies": { 13 | "deepl-node": "^1.7.1", 14 | "dotenv": "^16.0.3", 15 | "glob": "^8.0.3" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^18.11.3", 19 | "typescript": "^4.8.4" 20 | } 21 | }, 22 | "node_modules/@types/node": { 23 | "version": "18.11.3", 24 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz", 25 | "integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==" 26 | }, 27 | "node_modules/asynckit": { 28 | "version": "0.4.0", 29 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 30 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 31 | }, 32 | "node_modules/axios": { 33 | "version": "1.1.3", 34 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz", 35 | "integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==", 36 | "dependencies": { 37 | "follow-redirects": "^1.15.0", 38 | "form-data": "^4.0.0", 39 | "proxy-from-env": "^1.1.0" 40 | } 41 | }, 42 | "node_modules/axios/node_modules/form-data": { 43 | "version": "4.0.0", 44 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 45 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 46 | "dependencies": { 47 | "asynckit": "^0.4.0", 48 | "combined-stream": "^1.0.8", 49 | "mime-types": "^2.1.12" 50 | }, 51 | "engines": { 52 | "node": ">= 6" 53 | } 54 | }, 55 | "node_modules/balanced-match": { 56 | "version": "1.0.2", 57 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 58 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 59 | }, 60 | "node_modules/brace-expansion": { 61 | "version": "2.0.1", 62 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 63 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 64 | "dependencies": { 65 | "balanced-match": "^1.0.0" 66 | } 67 | }, 68 | "node_modules/combined-stream": { 69 | "version": "1.0.8", 70 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 71 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 72 | "dependencies": { 73 | "delayed-stream": "~1.0.0" 74 | }, 75 | "engines": { 76 | "node": ">= 0.8" 77 | } 78 | }, 79 | "node_modules/deepl-node": { 80 | "version": "1.7.1", 81 | "resolved": "https://registry.npmjs.org/deepl-node/-/deepl-node-1.7.1.tgz", 82 | "integrity": "sha512-ugd8S6VM/uDqFIeMt7Io7Cei6ckrvhY4xR4qrdMuG2XLZ/mO2jVlQ2XnuFjTI2PIVMkodMGe4kMCtIZlzi/5Qg==", 83 | "dependencies": { 84 | "@types/node": ">=12.0", 85 | "axios": ">=0.21.2", 86 | "form-data": "^3.0.0", 87 | "loglevel": ">=1.6.2" 88 | }, 89 | "engines": { 90 | "node": ">=12.0" 91 | } 92 | }, 93 | "node_modules/delayed-stream": { 94 | "version": "1.0.0", 95 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 96 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 97 | "engines": { 98 | "node": ">=0.4.0" 99 | } 100 | }, 101 | "node_modules/dotenv": { 102 | "version": "16.0.3", 103 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 104 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 105 | "engines": { 106 | "node": ">=12" 107 | } 108 | }, 109 | "node_modules/follow-redirects": { 110 | "version": "1.15.2", 111 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 112 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 113 | "funding": [ 114 | { 115 | "type": "individual", 116 | "url": "https://github.com/sponsors/RubenVerborgh" 117 | } 118 | ], 119 | "engines": { 120 | "node": ">=4.0" 121 | }, 122 | "peerDependenciesMeta": { 123 | "debug": { 124 | "optional": true 125 | } 126 | } 127 | }, 128 | "node_modules/form-data": { 129 | "version": "3.0.1", 130 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", 131 | "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", 132 | "dependencies": { 133 | "asynckit": "^0.4.0", 134 | "combined-stream": "^1.0.8", 135 | "mime-types": "^2.1.12" 136 | }, 137 | "engines": { 138 | "node": ">= 6" 139 | } 140 | }, 141 | "node_modules/fs.realpath": { 142 | "version": "1.0.0", 143 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 144 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 145 | }, 146 | "node_modules/glob": { 147 | "version": "8.0.3", 148 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", 149 | "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", 150 | "dependencies": { 151 | "fs.realpath": "^1.0.0", 152 | "inflight": "^1.0.4", 153 | "inherits": "2", 154 | "minimatch": "^5.0.1", 155 | "once": "^1.3.0" 156 | }, 157 | "engines": { 158 | "node": ">=12" 159 | }, 160 | "funding": { 161 | "url": "https://github.com/sponsors/isaacs" 162 | } 163 | }, 164 | "node_modules/inflight": { 165 | "version": "1.0.6", 166 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 167 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 168 | "dependencies": { 169 | "once": "^1.3.0", 170 | "wrappy": "1" 171 | } 172 | }, 173 | "node_modules/inherits": { 174 | "version": "2.0.4", 175 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 176 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 177 | }, 178 | "node_modules/loglevel": { 179 | "version": "1.8.0", 180 | "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", 181 | "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", 182 | "engines": { 183 | "node": ">= 0.6.0" 184 | }, 185 | "funding": { 186 | "type": "tidelift", 187 | "url": "https://tidelift.com/funding/github/npm/loglevel" 188 | } 189 | }, 190 | "node_modules/mime-db": { 191 | "version": "1.52.0", 192 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 193 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 194 | "engines": { 195 | "node": ">= 0.6" 196 | } 197 | }, 198 | "node_modules/mime-types": { 199 | "version": "2.1.35", 200 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 201 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 202 | "dependencies": { 203 | "mime-db": "1.52.0" 204 | }, 205 | "engines": { 206 | "node": ">= 0.6" 207 | } 208 | }, 209 | "node_modules/minimatch": { 210 | "version": "5.1.0", 211 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", 212 | "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", 213 | "dependencies": { 214 | "brace-expansion": "^2.0.1" 215 | }, 216 | "engines": { 217 | "node": ">=10" 218 | } 219 | }, 220 | "node_modules/once": { 221 | "version": "1.4.0", 222 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 223 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 224 | "dependencies": { 225 | "wrappy": "1" 226 | } 227 | }, 228 | "node_modules/proxy-from-env": { 229 | "version": "1.1.0", 230 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 231 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 232 | }, 233 | "node_modules/typescript": { 234 | "version": "4.8.4", 235 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", 236 | "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", 237 | "dev": true, 238 | "bin": { 239 | "tsc": "bin/tsc", 240 | "tsserver": "bin/tsserver" 241 | }, 242 | "engines": { 243 | "node": ">=4.2.0" 244 | } 245 | }, 246 | "node_modules/wrappy": { 247 | "version": "1.0.2", 248 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 249 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 250 | } 251 | }, 252 | "dependencies": { 253 | "@types/node": { 254 | "version": "18.11.3", 255 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz", 256 | "integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==" 257 | }, 258 | "asynckit": { 259 | "version": "0.4.0", 260 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 261 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 262 | }, 263 | "axios": { 264 | "version": "1.1.3", 265 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz", 266 | "integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==", 267 | "requires": { 268 | "follow-redirects": "^1.15.0", 269 | "form-data": "^4.0.0", 270 | "proxy-from-env": "^1.1.0" 271 | }, 272 | "dependencies": { 273 | "form-data": { 274 | "version": "4.0.0", 275 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 276 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 277 | "requires": { 278 | "asynckit": "^0.4.0", 279 | "combined-stream": "^1.0.8", 280 | "mime-types": "^2.1.12" 281 | } 282 | } 283 | } 284 | }, 285 | "balanced-match": { 286 | "version": "1.0.2", 287 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 288 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 289 | }, 290 | "brace-expansion": { 291 | "version": "2.0.1", 292 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 293 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 294 | "requires": { 295 | "balanced-match": "^1.0.0" 296 | } 297 | }, 298 | "combined-stream": { 299 | "version": "1.0.8", 300 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 301 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 302 | "requires": { 303 | "delayed-stream": "~1.0.0" 304 | } 305 | }, 306 | "deepl-node": { 307 | "version": "1.7.1", 308 | "resolved": "https://registry.npmjs.org/deepl-node/-/deepl-node-1.7.1.tgz", 309 | "integrity": "sha512-ugd8S6VM/uDqFIeMt7Io7Cei6ckrvhY4xR4qrdMuG2XLZ/mO2jVlQ2XnuFjTI2PIVMkodMGe4kMCtIZlzi/5Qg==", 310 | "requires": { 311 | "@types/node": ">=12.0", 312 | "axios": ">=0.21.2", 313 | "form-data": "^3.0.0", 314 | "loglevel": ">=1.6.2" 315 | } 316 | }, 317 | "delayed-stream": { 318 | "version": "1.0.0", 319 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 320 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 321 | }, 322 | "dotenv": { 323 | "version": "16.0.3", 324 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 325 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" 326 | }, 327 | "follow-redirects": { 328 | "version": "1.15.2", 329 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 330 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" 331 | }, 332 | "form-data": { 333 | "version": "3.0.1", 334 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", 335 | "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", 336 | "requires": { 337 | "asynckit": "^0.4.0", 338 | "combined-stream": "^1.0.8", 339 | "mime-types": "^2.1.12" 340 | } 341 | }, 342 | "fs.realpath": { 343 | "version": "1.0.0", 344 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 345 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 346 | }, 347 | "glob": { 348 | "version": "8.0.3", 349 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", 350 | "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", 351 | "requires": { 352 | "fs.realpath": "^1.0.0", 353 | "inflight": "^1.0.4", 354 | "inherits": "2", 355 | "minimatch": "^5.0.1", 356 | "once": "^1.3.0" 357 | } 358 | }, 359 | "inflight": { 360 | "version": "1.0.6", 361 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 362 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 363 | "requires": { 364 | "once": "^1.3.0", 365 | "wrappy": "1" 366 | } 367 | }, 368 | "inherits": { 369 | "version": "2.0.4", 370 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 371 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 372 | }, 373 | "loglevel": { 374 | "version": "1.8.0", 375 | "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", 376 | "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==" 377 | }, 378 | "mime-db": { 379 | "version": "1.52.0", 380 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 381 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 382 | }, 383 | "mime-types": { 384 | "version": "2.1.35", 385 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 386 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 387 | "requires": { 388 | "mime-db": "1.52.0" 389 | } 390 | }, 391 | "minimatch": { 392 | "version": "5.1.0", 393 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", 394 | "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", 395 | "requires": { 396 | "brace-expansion": "^2.0.1" 397 | } 398 | }, 399 | "once": { 400 | "version": "1.4.0", 401 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 402 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 403 | "requires": { 404 | "wrappy": "1" 405 | } 406 | }, 407 | "proxy-from-env": { 408 | "version": "1.1.0", 409 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 410 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 411 | }, 412 | "typescript": { 413 | "version": "4.8.4", 414 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", 415 | "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", 416 | "dev": true 417 | }, 418 | "wrappy": { 419 | "version": "1.0.2", 420 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 421 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 422 | } 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.ts", 6 | "scripts": { 7 | "start": "ts-node-esm src/index.ts", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@types/node": "^18.11.3", 15 | "typescript": "^4.8.4" 16 | }, 17 | "dependencies": { 18 | "deepl-node": "^1.7.1", 19 | "dotenv": "^16.0.3", 20 | "glob": "^8.0.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/converter.ts: -------------------------------------------------------------------------------- 1 | import File_ from "./file"; 2 | import Helper from "./helper"; 3 | import { ITranslateObject } from "./Translate"; 4 | 5 | 6 | // type Prop = 'label' | 'placeholder' | 'title'; 7 | const props = ['label', 'placeholder', 'title'] as const; 8 | type Prop = typeof props[number]; 9 | 10 | 11 | class Converter { 12 | 13 | jsx: string = ""; 14 | private comments: string[]; 15 | 16 | /* 17 | * set initial values 18 | */ 19 | 20 | constructor(p: string) { 21 | 22 | this.jsx = File_.read(p); 23 | // find lines that are most likely comments 24 | this.comments = this.jsx.split('\n').filter((line: string) => line.includes('//') || line.includes('*')); 25 | } 26 | 27 | /** 28 | * starts with > 29 | * ends with < / 31 | * multiple lines 32 | * */ 33 | 34 | private readonly IS_TEXT_REGEX: Readonly = />[^<>]*<\//gm; 35 | // has at least one letter 36 | private readonly HAS_LETTER_REGEX: Readonly = /[a-zA-Z]/gm; 37 | // find text between brackets {*} 38 | private readonly IS_VARIALBLE_REGEX: Readonly = /\{(.*?)\}/gm; 39 | 40 | 41 | /* * 42 | * placeholder | label space? = space? 43 | * may start with " or { or ' or ` 44 | * contain at least one letter between 45 | * must end with " or } or ' or ` 46 | * case insensetive 47 | * */ 48 | 49 | private readonly IS_PROP_REGEX: Readonly = /(placeholder|label|title)\s?=\s?\w*["{'](.*?)\w*['"}]/gim; 50 | 51 | /* * 52 | * check if text contains " / ' / ` 53 | * */ 54 | 55 | private readonly HAS_CHARS_REGEX: Readonly = /.*["'`].*/gm; 56 | 57 | /* * 58 | * check if text contains ( or ) or $ 59 | * */ 60 | 61 | private readonly HAS_SPECIAL_CHARS_REGEX: Readonly = /.*[()$+].*/gm; 62 | 63 | 64 | /* * 65 | * get only text between "" / '' / `` 66 | * remove chars 67 | * */ 68 | 69 | private readonly EXTRACT_TEXT_REGEX: Readonly = /(?<=["'`]).*?(?=["'`])/g; 70 | 71 | /* * 72 | * find default function name 73 | * const | function | bottom | direct export 74 | * */ 75 | 76 | private readonly DEFAULT_FUNC_REGEX: Readonly = /export default\s?(.*?)\s?(\(|;|=|{|^$)/gm; 77 | 78 | /* * 79 | * remove special chars from string 80 | * keep only alphabetics and numbers 81 | * */ 82 | 83 | private readonly NO_SPECIAL_CHARS_REGEX: Readonly = /[^a-zA-Z0-9]/g; 84 | 85 | 86 | /* * 87 | * remove special chars from string 88 | * keep only alphabetics and numbers 89 | * */ 90 | 91 | private readonly EXTRACT_YUP_REFS: Readonly = /yup.ref(.*?)\)/gim; 92 | 93 | /* * 94 | * find snackbar functions 95 | * */ 96 | 97 | private readonly IS_SNACKBAR: Readonly = /enqueueSnackbar\(["'](.*?)["']/gm; 98 | 99 | 100 | /* * 101 | * contains objects to be translated 102 | * */ 103 | 104 | translation: Object = {}; 105 | 106 | 107 | /** 108 | * Check if a comment 109 | * @param component (string) 110 | * @returns Boolean 111 | * */ 112 | 113 | 114 | private isComment = (component: string): Boolean => { 115 | return this.comments.some((c: string) => 116 | c.includes(component) || component.includes(c)) 117 | }; 118 | 119 | 120 | 121 | /** 122 | * Check if string has any of these chars 123 | * @param str (string) 124 | * @returns Boolean 125 | * */ 126 | 127 | 128 | private hasAnyOfChars = (str: string): Boolean => { 129 | 130 | // sometimes it fails only on nodejs for no good reason 131 | if (this.HAS_CHARS_REGEX.test(str)) return true; 132 | 133 | // extra verification 134 | if (str.includes('"')) return true 135 | if (str.includes("'")) return true 136 | if (str.includes('`')) return true 137 | 138 | return false; 139 | }; 140 | 141 | 142 | /** 143 | * Convert code using useTranslate 144 | * @returns new JSX 145 | * */ 146 | 147 | public convert = () => { 148 | this.replaceText(); 149 | this.replaceProps(); 150 | this.replaceYup(); 151 | this.replaceSnackbars(); 152 | 153 | // if there was nothing to replace then return; 154 | if (Object.values(this.translation).length == 0) return; 155 | 156 | this.importHook() 157 | this.setHook() 158 | } 159 | 160 | 161 | /** 162 | * get property name for string 163 | * @returns new name 164 | * */ 165 | 166 | 167 | private getPropertyName = (phrase: string): ITranslateObject => { 168 | 169 | // Remove single, double, or quotation marks from string 170 | phrase = phrase 171 | .trim() 172 | .replace(/['‘’"“”]/g, '') 173 | 174 | const propertyName = phrase 175 | .trim() 176 | // replace spaces with underscore 177 | .replace(/\s/g, '_') 178 | .split(':') 179 | // replace two dots with underscore 180 | .join('_'); 181 | 182 | return { 183 | phrase, 184 | propertyName, 185 | t: `{t("${propertyName}")}` 186 | } 187 | } 188 | 189 | 190 | /** 191 | * check if prop is placeholder / label / title 192 | * @returns prop name 193 | * */ 194 | 195 | 196 | private getPropName = (prop: string): Prop | undefined => { 197 | prop = prop.toLowerCase(); 198 | for (const p of props) { if (prop.includes(p)) return p }; 199 | return undefined; 200 | } 201 | 202 | 203 | 204 | /** 205 | * Convert code using useTranslate 206 | * @returns new JSX 207 | * */ 208 | 209 | private replaceText = (): void => { 210 | 211 | // filter results 212 | const result = (this.jsx.match(this.IS_TEXT_REGEX) || []) 213 | .filter((o: string) => 214 | o.match(this.HAS_LETTER_REGEX) 215 | && !this.HAS_SPECIAL_CHARS_REGEX.test(o)) 216 | .filter((o: string) => !this.isComment(o)); 217 | 218 | 219 | // start converting 220 | for (let i = 0; i < result.length; i++) { 221 | 222 | // removes whitespace / line breaks from the beginning and end of strings. 223 | let line = result[i] 224 | .substring(1, result[i].length - 2) 225 | .replace(/\s+/g, ' ') 226 | .trim(); 227 | 228 | // extract variables 229 | const variables: string[] | RegExpMatchArray | null = line.match(this.IS_VARIALBLE_REGEX); 230 | const phrases: string[] = line 231 | .split(this.IS_VARIALBLE_REGEX) 232 | .filter((word: string) => word.match(this.HAS_LETTER_REGEX)) 233 | 234 | // has variables 235 | for (let y = 0; y < phrases.length; y++) { 236 | let phrase = phrases[y]; 237 | 238 | let isVariable = false; 239 | if (variables) isVariable = variables?.includes(`{${phrase}}`); 240 | 241 | // ignore bad variable cuts 242 | if (Helper.isOdd( 243 | Helper.countOccurence(phrase, '{') + 244 | Helper.countOccurence(phrase, '}'))) continue; 245 | 246 | // extra check for arrow functions or child components conflicts 247 | if (phrase.includes('<') || phrase.includes('>')) continue; 248 | 249 | if (!isVariable) { 250 | const tObj: ITranslateObject = this.getPropertyName(phrase) 251 | // add to translator 252 | this.translation = { ...this.translation, [tObj.propertyName]: tObj.phrase }; 253 | // add t() 254 | phrase = tObj.t; 255 | } 256 | 257 | // add {} for variables 258 | else phrase = `{${phrase}}`; 259 | 260 | // replace 261 | phrases[y] = phrase; 262 | } 263 | 264 | // build final string 265 | line = `>${phrases.join(" ")} { 280 | 281 | // filter results 282 | const result = (this.jsx.match(this.IS_PROP_REGEX) || []) 283 | .filter((o: string) => this.hasAnyOfChars(o) && !o.includes("${")) 284 | .filter((o: string) => !this.isComment(o)) 285 | 286 | // start converting 287 | for (let i = 0; i < result.length; i++) { 288 | 289 | let line = result[i]; 290 | 291 | // if closing brackets is not captured then add it 292 | if (line.includes('{') && !line.includes('}')) result[i] += "}"; 293 | 294 | // extract text 295 | const text = (line.match(this.EXTRACT_TEXT_REGEX) || [])[0]; 296 | // has no chars or undefined 297 | if (!text || !text.match(this.HAS_LETTER_REGEX)) continue; 298 | 299 | const tObj: ITranslateObject = this.getPropertyName(text) 300 | this.translation = { ...this.translation, [tObj.propertyName]: tObj.phrase }; 301 | 302 | // build final string 303 | line = `${this.getPropName(result[i])}=${tObj.t}`; 304 | 305 | // find every result[i] and replace with line 306 | this.jsx = this.jsx.replace(result[i], line); 307 | } 308 | } 309 | 310 | 311 | 312 | /** 313 | * Replace props with translate hook 314 | * @returns new JSX 315 | * */ 316 | 317 | private replaceYup = (): void => { 318 | let replaced: string[] = []; 319 | 320 | const startAtIndex = this.jsx.indexOf("yup.object().shape({"); 321 | if (startAtIndex == -1) return; 322 | 323 | const endsAtIndex = this.jsx.indexOf("})", startAtIndex); 324 | if (endsAtIndex == -1) return; 325 | 326 | // get yup text 327 | const yup = this.jsx.substring(startAtIndex, endsAtIndex); 328 | // ignore yup refs 329 | const refs: string[] = (yup.match(this.EXTRACT_YUP_REFS) || []); 330 | 331 | let yup_ = yup; 332 | 333 | // extract yup strings 334 | const strings = yup.match(this.EXTRACT_TEXT_REGEX); 335 | if (!strings || strings.length == 0) return; 336 | 337 | for (let i = 0; i < strings.length; i++) { 338 | let str = strings[i]; 339 | 340 | // check if ref 341 | if (refs.some((ref: string) => ref.includes(str))) continue; 342 | // contain variables 343 | if (str.includes('{') || str.includes('}')) continue; 344 | 345 | const tObj: ITranslateObject = this.getPropertyName(str) 346 | 347 | // build final string 348 | str = tObj.t.substring(1, tObj.t.length - 1); 349 | if (replaced.includes(str)) continue; 350 | 351 | this.translation = { ...this.translation, [tObj.propertyName]: tObj.phrase }; 352 | 353 | // set new string in yup object 354 | replaced.push(str); 355 | yup_ = yup_.split(`"${tObj.phrase}"`).join(str); 356 | } 357 | 358 | // set new yup 359 | this.jsx = this.jsx.replace(yup, yup_); 360 | } 361 | 362 | 363 | 364 | /** 365 | * Replace snackbars with translate hook 366 | * @returns new JSX 367 | * */ 368 | 369 | private replaceSnackbars = (): void => { 370 | 371 | // filter results 372 | const result = (this.jsx.match(this.IS_SNACKBAR) || []) 373 | .filter((o: string) => !this.isComment(o)) 374 | 375 | // start converting 376 | for (let i = 0; i < result.length; i++) { 377 | 378 | let line = result[i]; 379 | 380 | // contain variables 381 | if (line.includes('{') || line.includes('}')) continue; 382 | 383 | // extract text 384 | const text = (line.match(this.EXTRACT_TEXT_REGEX) || [])[0]; 385 | if (!text || !text.match(this.HAS_LETTER_REGEX)) continue; 386 | 387 | 388 | const tObj: ITranslateObject = this.getPropertyName(text) 389 | this.translation = { ...this.translation, [tObj.propertyName]: tObj.phrase }; 390 | 391 | // find every result[i] and replace with line 392 | this.jsx = this.jsx.replace(result[i], `enqueueSnackbar(t("${tObj.propertyName}")`); 393 | } 394 | } 395 | 396 | 397 | 398 | /** 399 | * Import useTranslation hook 400 | * @returns new JSX 401 | * */ 402 | 403 | private importHook = (): void => { 404 | this.jsx = "import { useTranslation } from 'react-i18next' \n" + this.jsx; 405 | } 406 | 407 | 408 | 409 | /** 410 | * get default function name 411 | * @returns new JSX 412 | * */ 413 | 414 | private getDefaultFunction = (): string | undefined => { 415 | // find default export line 416 | const defaultExport = (this.jsx.match(this.DEFAULT_FUNC_REGEX) || [])[0]; 417 | if (!defaultExport) return; 418 | 419 | // remove special chars 420 | const defFunction = defaultExport 421 | .replace("function", "") 422 | .replace("export default ", "") 423 | .replace(this.NO_SPECIAL_CHARS_REGEX, "") 424 | .trim(); 425 | 426 | return defFunction; 427 | } 428 | 429 | 430 | 431 | /** 432 | * set useTranslation hook 433 | * @returns new JSX 434 | * */ 435 | 436 | private setHook = (): void => { 437 | 438 | // get default function name 439 | const defFunction = this.getDefaultFunction(); 440 | if (!defFunction) return; 441 | 442 | // extract function start ex : Login = () => { 443 | const DEFAULT_FUNC_START_REGEX = new RegExp(`${defFunction}\\s?[=(](.*?)[)](.*?){`, "gm"); 444 | 445 | // get index of func & the next line break index 446 | const func = (this.jsx.match(DEFAULT_FUNC_START_REGEX) || [])[0]; 447 | if (!func) return; 448 | 449 | const dfIndex = this.jsx.indexOf(func); 450 | const hookIndex = this.jsx.indexOf('\n', dfIndex); 451 | 452 | // add useState 453 | this.jsx = 454 | this.jsx.substring(0, hookIndex) 455 | + '\n const { t, i18n } = useTranslation();\n' 456 | + this.jsx.substring(hookIndex); 457 | } 458 | } 459 | 460 | 461 | 462 | export default Converter; -------------------------------------------------------------------------------- /src/directory.ts: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require('path'); 3 | const os = require('os'); 4 | const glob = require('glob'); 5 | 6 | 7 | class Directory { 8 | 9 | /** 10 | * @directory = /desktop/project 11 | * */ 12 | 13 | public static readonly PATH: string = path.join(os.homedir(), "Desktop", "project"); 14 | 15 | /** 16 | * Create direcotry if not exists 17 | * recursive so it create every dir in our path 18 | * @param dir (string) 19 | * @returns void 20 | * */ 21 | 22 | 23 | public static create(dir: string): void { 24 | // extract folder in case it's a filename 25 | dir = path.dirname(dir); 26 | // create if do not exists 27 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); 28 | } 29 | 30 | 31 | /** 32 | * Load jsx | tsx files 33 | * @param order (order) 34 | * @returns files 35 | * */ 36 | 37 | 38 | public static load(): string[] { 39 | return glob.sync(Directory.PATH + '/**/*(*.tsx|*.jsx)') 40 | // ignore .build folder 41 | .filter((f: string) => !f.includes("build")); 42 | } 43 | } 44 | 45 | 46 | 47 | export default Directory; -------------------------------------------------------------------------------- /src/file.ts: -------------------------------------------------------------------------------- 1 | import Directory from "./directory"; 2 | 3 | const fs = require("fs"); 4 | 5 | class File_ { 6 | 7 | /** 8 | * Read content from file 9 | * @param path (string) 10 | * @returns content (string) JSX 11 | * */ 12 | 13 | 14 | public static read(path: string): string { 15 | return fs.readFileSync(path).toString(); 16 | } 17 | 18 | 19 | /** 20 | * Write content to file 21 | * @param content (string) 22 | * @returns void 23 | * */ 24 | 25 | 26 | public static write(path: string, content: string): void { 27 | // create path if do not exists 28 | Directory.create(path); 29 | 30 | // write content 31 | fs.writeFile(path, content, function (err: any) { 32 | if (err) return console.log(err); 33 | }); 34 | } 35 | } 36 | 37 | 38 | 39 | export default File_; -------------------------------------------------------------------------------- /src/helper.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Helper { 4 | 5 | /** 6 | * Count char occurence in a given string 7 | * @param str (string) 8 | * @param letter (string) 9 | * @returns number 10 | * */ 11 | 12 | 13 | public static countOccurence(str: string, letter: string): number { 14 | let count = 0; 15 | 16 | // looping through the items 17 | for (let i = 0; i < str.length; i++) { 18 | // check if the character is at that position 19 | if (str.charAt(i) == letter) { 20 | count += 1; 21 | } 22 | } 23 | 24 | return count; 25 | } 26 | 27 | 28 | /** 29 | * Number is odd 30 | * @param number (number) 31 | * @returns boolean 32 | * */ 33 | 34 | 35 | public static isOdd(number: number): boolean { 36 | return (number % 2) == 1; 37 | } 38 | } 39 | 40 | 41 | 42 | export default Helper; -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import Converter from "./converter"; 2 | import Directory from "./directory"; 3 | import File_ from "./file"; 4 | import Translate, { ITranslate } from "./Translate"; 5 | 6 | 7 | 8 | class Main { 9 | 10 | toTranslate: ITranslate = { translation: {} }; 11 | 12 | constructor() { 13 | this.main() 14 | } 15 | 16 | 17 | public main(): void { 18 | const files: string[] = Directory.load(); 19 | console.log(files.length, ' file found.') 20 | 21 | files.forEach((file: string, i: number) => { 22 | process.stdout.write(`converting...[${i + 1}/${files.length}] file\r`) 23 | // convert file 24 | const converter = new Converter(file); 25 | converter.convert(); 26 | 27 | // write result 28 | File_.write(file, converter.jsx); 29 | 30 | // concat phrases to translate 31 | this.toTranslate.translation = { 32 | ...this.toTranslate.translation, 33 | ...converter.translation 34 | }; 35 | }); 36 | 37 | console.log("\ntranslating..."); 38 | // translate * 39 | Translate.fetch(this.toTranslate); 40 | } 41 | } 42 | 43 | 44 | new Main(); -------------------------------------------------------------------------------- /src/translate.ts: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const path = require('path'); 3 | 4 | import * as deepl from 'deepl-node'; 5 | import Directory from './directory'; 6 | import File_ from './file'; 7 | 8 | 9 | export interface ITranslateObject { 10 | phrase: string; 11 | propertyName: string; 12 | t: string; 13 | } 14 | 15 | 16 | export interface ITranslate { 17 | translation: Object | any; 18 | } 19 | 20 | 21 | class Translate { 22 | 23 | private static readonly from: deepl.SourceLanguageCode = 'en'; 24 | private static readonly to: deepl.TargetLanguageCode[] = ['en-US', 'fr']; 25 | private static translator = new deepl.Translator(process.env.AUTH_KEY || ''); 26 | 27 | 28 | /** 29 | * Translate text 30 | * @param q (string) 31 | * @returns string 32 | * */ 33 | 34 | 35 | private static async translate(str: string, target: deepl.TargetLanguageCode): Promise { 36 | const result = await this.translator.translateText(str, this.from, "fr"); 37 | return result.text; 38 | } 39 | 40 | 41 | /** 42 | * Translate text to target languages 43 | * @param str (string) 44 | * @returns translations (object) 45 | * */ 46 | 47 | 48 | private static async getAllTranslations(str: string[]): Promise { 49 | let translated: Object = {}; 50 | 51 | for (let i = 0; i < this.to.length; i++) { 52 | 53 | // if source language == target 54 | if (this.to[i].includes(this.from)) { 55 | 56 | // keep original 57 | translated = { 58 | ...translated, 59 | [this.to[i]]: str 60 | } 61 | 62 | continue; 63 | }; 64 | 65 | /** 66 | * @LIMIT_SIZE 67 | * The total deepl request body size must not exceed 128 KiB (128 · 1024 bytes). 68 | * Please split up your text into multiple calls if it exceeds this limit. 69 | **/ 70 | 71 | const batch_size = 20; 72 | let res: string[] = []; 73 | 74 | for (let y = 0; y < str.length; y += batch_size) { 75 | 76 | const batch = new Array(...str).splice(y, batch_size); 77 | if (batch.length == 0) break; 78 | 79 | const text = batch.join(''); 80 | let result = await this.translate(text, this.to[i]); 81 | 82 | // concat result 83 | res = [...res, ...result.split('')]; 84 | } 85 | 86 | // set language result 87 | translated = { 88 | ...translated, 89 | [this.to[i]]: res 90 | } 91 | } 92 | 93 | return translated; 94 | } 95 | 96 | 97 | /** 98 | * Translate text to target languages 99 | * @param str (string) 100 | * @returns translations (object) 101 | * */ 102 | 103 | 104 | public static fetch(ts: ITranslate): void { 105 | // convert object array to string 106 | const text: string[] = Object.values(ts.translation); 107 | 108 | 109 | this.getAllTranslations(text) 110 | .then((res: Object) => this.createJSON(ts, res)) 111 | .catch((e: any) => console.log(e)) 112 | } 113 | 114 | 115 | 116 | /** 117 | * Generate json file 118 | * @ex [{ en: '', es: ''... }] 119 | * @param str (string) 120 | * @returns JSON 121 | * */ 122 | 123 | 124 | private static createJSON(ts: ITranslate, translations: Object | any): void { 125 | 126 | this.to.forEach((lang: string) => { 127 | try { 128 | const json = ts; 129 | if (!translations[lang]) return; 130 | 131 | const text: string[] = translations[lang]; 132 | 133 | // get english property names 134 | Object.keys(json.translation).forEach(function (key, i) { 135 | json.translation[key] = text[i] 136 | }); 137 | 138 | // write file 139 | File_.write(path.join(Directory.PATH, "lang", `${lang}.json`), JSON.stringify(json)); 140 | } 141 | catch (e) { console.log(e) } 142 | }); 143 | 144 | } 145 | } 146 | 147 | 148 | 149 | export default Translate; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | "lib": ["es6"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | "rootDir": "src", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | "resolveJsonModule": true, /* Enable importing .json files. */ 39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 40 | 41 | /* JavaScript Support */ 42 | "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 45 | 46 | /* Emit */ 47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 52 | "outDir": "build", /* Specify an output folder for all emitted files. */ 53 | // "removeComments": true, /* Disable emitting comments. */ 54 | // "noEmit": true, /* Disable emitting files from a compilation. */ 55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 63 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 70 | 71 | /* Interop Constraints */ 72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 76 | //"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 77 | 78 | /* Type Checking */ 79 | "strict": true, /* Enable all strict type-checking options. */ 80 | "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 98 | 99 | /* Completeness */ 100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 101 | //"skipLibCheck": true /* Skip type checking all .d.ts files. */ 102 | } 103 | } 104 | --------------------------------------------------------------------------------