lit 0.4.0.pre.alpha.5 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (296) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +89 -5
  3. data/Rakefile +9 -15
  4. data/app/assets/javascripts/lit/backend/bootstrap.min.js +7 -0
  5. data/app/controllers/lit/api/v1/localizations_controller.rb +12 -15
  6. data/app/controllers/lit/cloud_translations_controller.rb +26 -0
  7. data/app/controllers/lit/localization_keys_controller.rb +6 -0
  8. data/app/controllers/lit/localizations_controller.rb +3 -2
  9. data/app/controllers/lit/sources_controller.rb +1 -1
  10. data/app/helpers/lit/frontend_helper.rb +1 -1
  11. data/app/helpers/lit/localizations_helper.rb +13 -0
  12. data/app/models/lit/base.rb +33 -17
  13. data/app/models/lit/incomming_localization.rb +13 -27
  14. data/app/models/lit/localization.rb +6 -14
  15. data/app/services/remote_interactor_service.rb +3 -1
  16. data/app/services/synchronize_source_service.rb +4 -8
  17. data/app/views/layouts/lit/application.html.erb +0 -1
  18. data/app/views/lit/cloud_translations/show.js.erb +14 -0
  19. data/app/views/lit/dashboard/index.html.erb +1 -1
  20. data/app/views/lit/incomming_localizations/index.html.erb +1 -1
  21. data/app/views/lit/localization_keys/_localizations_list.html.erb +1 -1
  22. data/app/views/lit/localization_keys/index.html.erb +2 -0
  23. data/app/views/lit/localization_keys/not_translated.html.erb +2 -0
  24. data/app/views/lit/localization_keys/visited_again.html.erb +3 -0
  25. data/app/views/lit/localizations/_form.html.erb +27 -0
  26. data/config/routes.rb +2 -0
  27. data/lib/generators/lit/install/templates/initializer.rb +6 -3
  28. data/lib/lit.rb +19 -6
  29. data/lib/lit/adapters/hash_storage.rb +35 -1
  30. data/lib/lit/adapters/redis_storage.rb +42 -7
  31. data/lib/lit/cache.rb +66 -10
  32. data/lib/lit/cloud_translation.rb +53 -0
  33. data/lib/lit/cloud_translation/providers/base.rb +66 -0
  34. data/lib/lit/cloud_translation/providers/google.rb +117 -0
  35. data/lib/lit/cloud_translation/providers/yandex.rb +84 -0
  36. data/lib/lit/engine.rb +11 -5
  37. data/lib/lit/export.rb +2 -18
  38. data/lib/lit/i18n_backend.rb +20 -7
  39. data/lib/lit/import.rb +15 -14
  40. data/lib/lit/loader.rb +3 -1
  41. data/lib/lit/middleware.rb +29 -0
  42. data/lib/lit/rails.rb +1 -0
  43. data/lib/lit/railtie.rb +6 -7
  44. data/lib/lit/services/localization_keys_to_hash_service.rb +23 -0
  45. data/lib/lit/version.rb +1 -1
  46. metadata +125 -268
  47. data/app/assets/images/lit/famfamfam_flags/ad.png +0 -0
  48. data/app/assets/images/lit/famfamfam_flags/ae.png +0 -0
  49. data/app/assets/images/lit/famfamfam_flags/af.png +0 -0
  50. data/app/assets/images/lit/famfamfam_flags/ag.png +0 -0
  51. data/app/assets/images/lit/famfamfam_flags/ai.png +0 -0
  52. data/app/assets/images/lit/famfamfam_flags/al.png +0 -0
  53. data/app/assets/images/lit/famfamfam_flags/am.png +0 -0
  54. data/app/assets/images/lit/famfamfam_flags/an.png +0 -0
  55. data/app/assets/images/lit/famfamfam_flags/ao.png +0 -0
  56. data/app/assets/images/lit/famfamfam_flags/ar.png +0 -0
  57. data/app/assets/images/lit/famfamfam_flags/as.png +0 -0
  58. data/app/assets/images/lit/famfamfam_flags/at.png +0 -0
  59. data/app/assets/images/lit/famfamfam_flags/au.png +0 -0
  60. data/app/assets/images/lit/famfamfam_flags/aw.png +0 -0
  61. data/app/assets/images/lit/famfamfam_flags/ax.png +0 -0
  62. data/app/assets/images/lit/famfamfam_flags/az.png +0 -0
  63. data/app/assets/images/lit/famfamfam_flags/ba.png +0 -0
  64. data/app/assets/images/lit/famfamfam_flags/bb.png +0 -0
  65. data/app/assets/images/lit/famfamfam_flags/bd.png +0 -0
  66. data/app/assets/images/lit/famfamfam_flags/be.png +0 -0
  67. data/app/assets/images/lit/famfamfam_flags/bf.png +0 -0
  68. data/app/assets/images/lit/famfamfam_flags/bg.png +0 -0
  69. data/app/assets/images/lit/famfamfam_flags/bh.png +0 -0
  70. data/app/assets/images/lit/famfamfam_flags/bi.png +0 -0
  71. data/app/assets/images/lit/famfamfam_flags/bj.png +0 -0
  72. data/app/assets/images/lit/famfamfam_flags/bm.png +0 -0
  73. data/app/assets/images/lit/famfamfam_flags/bn.png +0 -0
  74. data/app/assets/images/lit/famfamfam_flags/bo.png +0 -0
  75. data/app/assets/images/lit/famfamfam_flags/br.png +0 -0
  76. data/app/assets/images/lit/famfamfam_flags/bs.png +0 -0
  77. data/app/assets/images/lit/famfamfam_flags/bt.png +0 -0
  78. data/app/assets/images/lit/famfamfam_flags/bv.png +0 -0
  79. data/app/assets/images/lit/famfamfam_flags/bw.png +0 -0
  80. data/app/assets/images/lit/famfamfam_flags/by.png +0 -0
  81. data/app/assets/images/lit/famfamfam_flags/bz.png +0 -0
  82. data/app/assets/images/lit/famfamfam_flags/ca.png +0 -0
  83. data/app/assets/images/lit/famfamfam_flags/catalonia.png +0 -0
  84. data/app/assets/images/lit/famfamfam_flags/cc.png +0 -0
  85. data/app/assets/images/lit/famfamfam_flags/cd.png +0 -0
  86. data/app/assets/images/lit/famfamfam_flags/cf.png +0 -0
  87. data/app/assets/images/lit/famfamfam_flags/cg.png +0 -0
  88. data/app/assets/images/lit/famfamfam_flags/ch.png +0 -0
  89. data/app/assets/images/lit/famfamfam_flags/ci.png +0 -0
  90. data/app/assets/images/lit/famfamfam_flags/ck.png +0 -0
  91. data/app/assets/images/lit/famfamfam_flags/cl.png +0 -0
  92. data/app/assets/images/lit/famfamfam_flags/cm.png +0 -0
  93. data/app/assets/images/lit/famfamfam_flags/cn.png +0 -0
  94. data/app/assets/images/lit/famfamfam_flags/co.png +0 -0
  95. data/app/assets/images/lit/famfamfam_flags/cr.png +0 -0
  96. data/app/assets/images/lit/famfamfam_flags/cs.png +0 -0
  97. data/app/assets/images/lit/famfamfam_flags/cu.png +0 -0
  98. data/app/assets/images/lit/famfamfam_flags/cv.png +0 -0
  99. data/app/assets/images/lit/famfamfam_flags/cx.png +0 -0
  100. data/app/assets/images/lit/famfamfam_flags/cy.png +0 -0
  101. data/app/assets/images/lit/famfamfam_flags/cz.png +0 -0
  102. data/app/assets/images/lit/famfamfam_flags/da.png +0 -0
  103. data/app/assets/images/lit/famfamfam_flags/de.png +0 -0
  104. data/app/assets/images/lit/famfamfam_flags/dj.png +0 -0
  105. data/app/assets/images/lit/famfamfam_flags/dk.png +0 -0
  106. data/app/assets/images/lit/famfamfam_flags/dm.png +0 -0
  107. data/app/assets/images/lit/famfamfam_flags/do.png +0 -0
  108. data/app/assets/images/lit/famfamfam_flags/dz.png +0 -0
  109. data/app/assets/images/lit/famfamfam_flags/ec.png +0 -0
  110. data/app/assets/images/lit/famfamfam_flags/ee.png +0 -0
  111. data/app/assets/images/lit/famfamfam_flags/eg.png +0 -0
  112. data/app/assets/images/lit/famfamfam_flags/eh.png +0 -0
  113. data/app/assets/images/lit/famfamfam_flags/en.png +0 -0
  114. data/app/assets/images/lit/famfamfam_flags/england.png +0 -0
  115. data/app/assets/images/lit/famfamfam_flags/er.png +0 -0
  116. data/app/assets/images/lit/famfamfam_flags/es.png +0 -0
  117. data/app/assets/images/lit/famfamfam_flags/et.png +0 -0
  118. data/app/assets/images/lit/famfamfam_flags/europeanunion.png +0 -0
  119. data/app/assets/images/lit/famfamfam_flags/fam.png +0 -0
  120. data/app/assets/images/lit/famfamfam_flags/fi.png +0 -0
  121. data/app/assets/images/lit/famfamfam_flags/fj.png +0 -0
  122. data/app/assets/images/lit/famfamfam_flags/fk.png +0 -0
  123. data/app/assets/images/lit/famfamfam_flags/fm.png +0 -0
  124. data/app/assets/images/lit/famfamfam_flags/fo.png +0 -0
  125. data/app/assets/images/lit/famfamfam_flags/fr.png +0 -0
  126. data/app/assets/images/lit/famfamfam_flags/ga.png +0 -0
  127. data/app/assets/images/lit/famfamfam_flags/gb.png +0 -0
  128. data/app/assets/images/lit/famfamfam_flags/gd.png +0 -0
  129. data/app/assets/images/lit/famfamfam_flags/ge.png +0 -0
  130. data/app/assets/images/lit/famfamfam_flags/gf.png +0 -0
  131. data/app/assets/images/lit/famfamfam_flags/gh.png +0 -0
  132. data/app/assets/images/lit/famfamfam_flags/gi.png +0 -0
  133. data/app/assets/images/lit/famfamfam_flags/gl.png +0 -0
  134. data/app/assets/images/lit/famfamfam_flags/gm.png +0 -0
  135. data/app/assets/images/lit/famfamfam_flags/gn.png +0 -0
  136. data/app/assets/images/lit/famfamfam_flags/gp.png +0 -0
  137. data/app/assets/images/lit/famfamfam_flags/gq.png +0 -0
  138. data/app/assets/images/lit/famfamfam_flags/gr.png +0 -0
  139. data/app/assets/images/lit/famfamfam_flags/gs.png +0 -0
  140. data/app/assets/images/lit/famfamfam_flags/gt.png +0 -0
  141. data/app/assets/images/lit/famfamfam_flags/gu.png +0 -0
  142. data/app/assets/images/lit/famfamfam_flags/gw.png +0 -0
  143. data/app/assets/images/lit/famfamfam_flags/gy.png +0 -0
  144. data/app/assets/images/lit/famfamfam_flags/hk.png +0 -0
  145. data/app/assets/images/lit/famfamfam_flags/hm.png +0 -0
  146. data/app/assets/images/lit/famfamfam_flags/hn.png +0 -0
  147. data/app/assets/images/lit/famfamfam_flags/hr.png +0 -0
  148. data/app/assets/images/lit/famfamfam_flags/ht.png +0 -0
  149. data/app/assets/images/lit/famfamfam_flags/hu.png +0 -0
  150. data/app/assets/images/lit/famfamfam_flags/id.png +0 -0
  151. data/app/assets/images/lit/famfamfam_flags/ie.png +0 -0
  152. data/app/assets/images/lit/famfamfam_flags/il.png +0 -0
  153. data/app/assets/images/lit/famfamfam_flags/in.png +0 -0
  154. data/app/assets/images/lit/famfamfam_flags/io.png +0 -0
  155. data/app/assets/images/lit/famfamfam_flags/iq.png +0 -0
  156. data/app/assets/images/lit/famfamfam_flags/ir.png +0 -0
  157. data/app/assets/images/lit/famfamfam_flags/is.png +0 -0
  158. data/app/assets/images/lit/famfamfam_flags/it.png +0 -0
  159. data/app/assets/images/lit/famfamfam_flags/ja.png +0 -0
  160. data/app/assets/images/lit/famfamfam_flags/jm.png +0 -0
  161. data/app/assets/images/lit/famfamfam_flags/jo.png +0 -0
  162. data/app/assets/images/lit/famfamfam_flags/jp.png +0 -0
  163. data/app/assets/images/lit/famfamfam_flags/ke.png +0 -0
  164. data/app/assets/images/lit/famfamfam_flags/kg.png +0 -0
  165. data/app/assets/images/lit/famfamfam_flags/kh.png +0 -0
  166. data/app/assets/images/lit/famfamfam_flags/ki.png +0 -0
  167. data/app/assets/images/lit/famfamfam_flags/km.png +0 -0
  168. data/app/assets/images/lit/famfamfam_flags/kn.png +0 -0
  169. data/app/assets/images/lit/famfamfam_flags/kp.png +0 -0
  170. data/app/assets/images/lit/famfamfam_flags/kr.png +0 -0
  171. data/app/assets/images/lit/famfamfam_flags/kw.png +0 -0
  172. data/app/assets/images/lit/famfamfam_flags/ky.png +0 -0
  173. data/app/assets/images/lit/famfamfam_flags/kz.png +0 -0
  174. data/app/assets/images/lit/famfamfam_flags/la.png +0 -0
  175. data/app/assets/images/lit/famfamfam_flags/lb.png +0 -0
  176. data/app/assets/images/lit/famfamfam_flags/lc.png +0 -0
  177. data/app/assets/images/lit/famfamfam_flags/li.png +0 -0
  178. data/app/assets/images/lit/famfamfam_flags/lk.png +0 -0
  179. data/app/assets/images/lit/famfamfam_flags/lr.png +0 -0
  180. data/app/assets/images/lit/famfamfam_flags/ls.png +0 -0
  181. data/app/assets/images/lit/famfamfam_flags/lt.png +0 -0
  182. data/app/assets/images/lit/famfamfam_flags/lu.png +0 -0
  183. data/app/assets/images/lit/famfamfam_flags/lv.png +0 -0
  184. data/app/assets/images/lit/famfamfam_flags/ly.png +0 -0
  185. data/app/assets/images/lit/famfamfam_flags/ma.png +0 -0
  186. data/app/assets/images/lit/famfamfam_flags/mc.png +0 -0
  187. data/app/assets/images/lit/famfamfam_flags/md.png +0 -0
  188. data/app/assets/images/lit/famfamfam_flags/me.png +0 -0
  189. data/app/assets/images/lit/famfamfam_flags/mg.png +0 -0
  190. data/app/assets/images/lit/famfamfam_flags/mh.png +0 -0
  191. data/app/assets/images/lit/famfamfam_flags/mk.png +0 -0
  192. data/app/assets/images/lit/famfamfam_flags/ml.png +0 -0
  193. data/app/assets/images/lit/famfamfam_flags/mm.png +0 -0
  194. data/app/assets/images/lit/famfamfam_flags/mn.png +0 -0
  195. data/app/assets/images/lit/famfamfam_flags/mo.png +0 -0
  196. data/app/assets/images/lit/famfamfam_flags/mp.png +0 -0
  197. data/app/assets/images/lit/famfamfam_flags/mq.png +0 -0
  198. data/app/assets/images/lit/famfamfam_flags/mr.png +0 -0
  199. data/app/assets/images/lit/famfamfam_flags/ms.png +0 -0
  200. data/app/assets/images/lit/famfamfam_flags/mt.png +0 -0
  201. data/app/assets/images/lit/famfamfam_flags/mu.png +0 -0
  202. data/app/assets/images/lit/famfamfam_flags/mv.png +0 -0
  203. data/app/assets/images/lit/famfamfam_flags/mw.png +0 -0
  204. data/app/assets/images/lit/famfamfam_flags/mx.png +0 -0
  205. data/app/assets/images/lit/famfamfam_flags/my.png +0 -0
  206. data/app/assets/images/lit/famfamfam_flags/mz.png +0 -0
  207. data/app/assets/images/lit/famfamfam_flags/na.png +0 -0
  208. data/app/assets/images/lit/famfamfam_flags/nc.png +0 -0
  209. data/app/assets/images/lit/famfamfam_flags/ne.png +0 -0
  210. data/app/assets/images/lit/famfamfam_flags/nf.png +0 -0
  211. data/app/assets/images/lit/famfamfam_flags/ng.png +0 -0
  212. data/app/assets/images/lit/famfamfam_flags/ni.png +0 -0
  213. data/app/assets/images/lit/famfamfam_flags/nl.png +0 -0
  214. data/app/assets/images/lit/famfamfam_flags/no.png +0 -0
  215. data/app/assets/images/lit/famfamfam_flags/np.png +0 -0
  216. data/app/assets/images/lit/famfamfam_flags/nr.png +0 -0
  217. data/app/assets/images/lit/famfamfam_flags/nu.png +0 -0
  218. data/app/assets/images/lit/famfamfam_flags/nz.png +0 -0
  219. data/app/assets/images/lit/famfamfam_flags/om.png +0 -0
  220. data/app/assets/images/lit/famfamfam_flags/pa.png +0 -0
  221. data/app/assets/images/lit/famfamfam_flags/pe.png +0 -0
  222. data/app/assets/images/lit/famfamfam_flags/pf.png +0 -0
  223. data/app/assets/images/lit/famfamfam_flags/pg.png +0 -0
  224. data/app/assets/images/lit/famfamfam_flags/ph.png +0 -0
  225. data/app/assets/images/lit/famfamfam_flags/pk.png +0 -0
  226. data/app/assets/images/lit/famfamfam_flags/pl.png +0 -0
  227. data/app/assets/images/lit/famfamfam_flags/pm.png +0 -0
  228. data/app/assets/images/lit/famfamfam_flags/pn.png +0 -0
  229. data/app/assets/images/lit/famfamfam_flags/pr.png +0 -0
  230. data/app/assets/images/lit/famfamfam_flags/ps.png +0 -0
  231. data/app/assets/images/lit/famfamfam_flags/pt.png +0 -0
  232. data/app/assets/images/lit/famfamfam_flags/pw.png +0 -0
  233. data/app/assets/images/lit/famfamfam_flags/py.png +0 -0
  234. data/app/assets/images/lit/famfamfam_flags/qa.png +0 -0
  235. data/app/assets/images/lit/famfamfam_flags/re.png +0 -0
  236. data/app/assets/images/lit/famfamfam_flags/ro.png +0 -0
  237. data/app/assets/images/lit/famfamfam_flags/rs.png +0 -0
  238. data/app/assets/images/lit/famfamfam_flags/ru.png +0 -0
  239. data/app/assets/images/lit/famfamfam_flags/rw.png +0 -0
  240. data/app/assets/images/lit/famfamfam_flags/sa.png +0 -0
  241. data/app/assets/images/lit/famfamfam_flags/sb.png +0 -0
  242. data/app/assets/images/lit/famfamfam_flags/sc.png +0 -0
  243. data/app/assets/images/lit/famfamfam_flags/scotland.png +0 -0
  244. data/app/assets/images/lit/famfamfam_flags/sd.png +0 -0
  245. data/app/assets/images/lit/famfamfam_flags/se.png +0 -0
  246. data/app/assets/images/lit/famfamfam_flags/sg.png +0 -0
  247. data/app/assets/images/lit/famfamfam_flags/sh.png +0 -0
  248. data/app/assets/images/lit/famfamfam_flags/si.png +0 -0
  249. data/app/assets/images/lit/famfamfam_flags/sj.png +0 -0
  250. data/app/assets/images/lit/famfamfam_flags/sk.png +0 -0
  251. data/app/assets/images/lit/famfamfam_flags/sl.png +0 -0
  252. data/app/assets/images/lit/famfamfam_flags/sm.png +0 -0
  253. data/app/assets/images/lit/famfamfam_flags/sn.png +0 -0
  254. data/app/assets/images/lit/famfamfam_flags/so.png +0 -0
  255. data/app/assets/images/lit/famfamfam_flags/sr.png +0 -0
  256. data/app/assets/images/lit/famfamfam_flags/st.png +0 -0
  257. data/app/assets/images/lit/famfamfam_flags/sv.png +0 -0
  258. data/app/assets/images/lit/famfamfam_flags/sy.png +0 -0
  259. data/app/assets/images/lit/famfamfam_flags/sz.png +0 -0
  260. data/app/assets/images/lit/famfamfam_flags/tc.png +0 -0
  261. data/app/assets/images/lit/famfamfam_flags/td.png +0 -0
  262. data/app/assets/images/lit/famfamfam_flags/tf.png +0 -0
  263. data/app/assets/images/lit/famfamfam_flags/tg.png +0 -0
  264. data/app/assets/images/lit/famfamfam_flags/th.png +0 -0
  265. data/app/assets/images/lit/famfamfam_flags/tj.png +0 -0
  266. data/app/assets/images/lit/famfamfam_flags/tk.png +0 -0
  267. data/app/assets/images/lit/famfamfam_flags/tl.png +0 -0
  268. data/app/assets/images/lit/famfamfam_flags/tm.png +0 -0
  269. data/app/assets/images/lit/famfamfam_flags/tn.png +0 -0
  270. data/app/assets/images/lit/famfamfam_flags/to.png +0 -0
  271. data/app/assets/images/lit/famfamfam_flags/tr.png +0 -0
  272. data/app/assets/images/lit/famfamfam_flags/tt.png +0 -0
  273. data/app/assets/images/lit/famfamfam_flags/tv.png +0 -0
  274. data/app/assets/images/lit/famfamfam_flags/tw.png +0 -0
  275. data/app/assets/images/lit/famfamfam_flags/tz.png +0 -0
  276. data/app/assets/images/lit/famfamfam_flags/ua.png +0 -0
  277. data/app/assets/images/lit/famfamfam_flags/ug.png +0 -0
  278. data/app/assets/images/lit/famfamfam_flags/um.png +0 -0
  279. data/app/assets/images/lit/famfamfam_flags/us.png +0 -0
  280. data/app/assets/images/lit/famfamfam_flags/uy.png +0 -0
  281. data/app/assets/images/lit/famfamfam_flags/uz.png +0 -0
  282. data/app/assets/images/lit/famfamfam_flags/va.png +0 -0
  283. data/app/assets/images/lit/famfamfam_flags/vc.png +0 -0
  284. data/app/assets/images/lit/famfamfam_flags/ve.png +0 -0
  285. data/app/assets/images/lit/famfamfam_flags/vg.png +0 -0
  286. data/app/assets/images/lit/famfamfam_flags/vi.png +0 -0
  287. data/app/assets/images/lit/famfamfam_flags/vn.png +0 -0
  288. data/app/assets/images/lit/famfamfam_flags/vu.png +0 -0
  289. data/app/assets/images/lit/famfamfam_flags/wales.png +0 -0
  290. data/app/assets/images/lit/famfamfam_flags/wf.png +0 -0
  291. data/app/assets/images/lit/famfamfam_flags/ws.png +0 -0
  292. data/app/assets/images/lit/famfamfam_flags/ye.png +0 -0
  293. data/app/assets/images/lit/famfamfam_flags/yt.png +0 -0
  294. data/app/assets/images/lit/famfamfam_flags/za.png +0 -0
  295. data/app/assets/images/lit/famfamfam_flags/zm.png +0 -0
  296. data/app/assets/images/lit/famfamfam_flags/zw.png +0 -0
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lit
4
+ module CloudTranslation
5
+ class TranslationError < StandardError; end
6
+
7
+ module_function
8
+
9
+ # Sets the provider for cloud translations.
10
+ # @param [Class] provider Selected translation provider,
11
+ # descending from Lit::CloudTranslation::Providers::Base
12
+ def provider=(provider)
13
+ @provider = provider
14
+ end
15
+
16
+ # Returns the currently active cloud translation provider,
17
+ # descending from Lit::CloudTranslation::Providers::Base.
18
+ def provider
19
+ @provider
20
+ end
21
+
22
+ # Uses the active translation provider to translate a text or array of
23
+ # texts.
24
+ # @param [String, Array] text The text (or array of texts) to translate
25
+ # @param [Symbol, String] from The language to translate from. If not given,
26
+ # auto-detection will be attempted.
27
+ # @param [Symbol, String] to The language to translate to.
28
+ # @param [Hash] opts Additional, provider-specific optional parameters.
29
+ def translate(text:, from: nil, to:, **opts)
30
+ provider.translate(text: text, from: from, to: to, **opts)
31
+ end
32
+
33
+ # Optional if provider-speciffic environment variables are set correctly.
34
+ # Configures the cloud translation provider with specific settings,
35
+ # overriding those from environment if needed.
36
+ #
37
+ # @example
38
+ # Lit::CloudTranslation.configure do |config|
39
+ # # For Yandex, this overrides the YANDEX_TRANSLATE_API_KEY env
40
+ # config.api_key = 'my_awesome_api_key'
41
+ # end
42
+ def configure(&block)
43
+ unless provider
44
+ raise 'Translation provider not selected yet. Use `Lit::CloudTranslation' \
45
+ '.provider = PROVIDER_KLASS` before calling #configure.'
46
+ end
47
+ provider.tap do |p|
48
+ p.configure(&block)
49
+ end
50
+ end
51
+ end
52
+ end # descending from Lit::CloudTranslation::Providers::Base.
53
+
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lit::CloudTranslation::Providers
4
+ # Abstract base class for cloud translation providers, providing a skeleton
5
+ # for the provider's functionality (mainly the #translate method) as well as
6
+ # a configuration management mechanism.
7
+ class Base
8
+ attr_reader :config
9
+
10
+ def initialize(config)
11
+ default_config.each do |key, value|
12
+ config[key] ||= value
13
+ end
14
+ @config = config
15
+ end
16
+
17
+ # Translates a given text from a given language to a different one.
18
+ # @param [String, Array] text The text (or array of texts) to translate
19
+ # @param [Symbol, String] from The language to translate from. If not given,
20
+ # auto-detection will be attempted.
21
+ # @param [Symbol, String] to The language to translate to.
22
+ # @param [Hash] opts Additional, provider-specific optional parameters.
23
+ def translate(text:, from: nil, to:, **opts) # rubocop:disable Lint/UnusedMethodArgument, Metrics/LineLength
24
+ raise NotImplementedError
25
+ end
26
+
27
+ private
28
+
29
+ # Loads specific information from environment variables or other sources
30
+ # as the default configuartion for the translation provider.
31
+ #
32
+ # This can be overridden using `Lit::CloudTranslation.configure`.
33
+ def default_config
34
+ {}
35
+ end
36
+
37
+ private_class_method :new
38
+
39
+ class << self
40
+ # Using the provider object's singleton instance, translates a given text from
41
+ # a given language to a different one.
42
+ # @param [String, Array] text The text (or array of texts) to translate
43
+ # @param [Symbol, String] from The language to translate from. If not given,
44
+ # auto-detection will be attempted.
45
+ # @param [Symbol, String] to The language to translate to.
46
+ # @param [Hash] opts Additional, provider-specific optional parameters.
47
+ def translate(text:, from: nil, to:, **opts)
48
+ instance.translate(text: text, from: from, to: to, **opts)
49
+ end
50
+
51
+ def configure
52
+ yield config if block_given?
53
+ end
54
+
55
+ private
56
+
57
+ def instance
58
+ @instance ||= new(config)
59
+ end
60
+
61
+ def config
62
+ @config ||= OpenStruct.new
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ begin
5
+ require 'google/cloud/translate'
6
+ rescue LoadError
7
+ raise StandardError, 'You need to add "gem \'google-cloud-translate\'" to your Gemfile to support Google Cloud Translation'
8
+ end
9
+
10
+ module Lit::CloudTranslation::Providers
11
+ # Google Cloud Translation API provider for Lit translation suggestions.
12
+ #
13
+ # Configuration:
14
+ #
15
+ # require 'lit/cloud_translation/providers/google'
16
+ #
17
+ # Lit::CloudTranslation.provider = Lit::CloudTranslation::Providers::Google
18
+ #
19
+ # # Service account configuration can be given via a file pointed to by
20
+ # # ENV['GOOGLE_TRANSLATE_API_KEYFILE'] (see here:
21
+ # # https://cloud.google.com/iam/docs/creating-managing-service-account-keys)
22
+ # #
23
+ # # Instead of providing the keyfile, credentials can be given using
24
+ # # GOOGLE_TRANSLATE_API_<element> environment variables, where e.g.
25
+ # # the GOOGLE_TRANSLATE_API_PROJECT_ID variable corresponds to the
26
+ # # `project_id` element of your credentials. Typically, only the following
27
+ # # variables are mandatory:
28
+ # # * GOOGLE_TRANSLATE_API_PROJECT_ID
29
+ # # * GOOGLE_TRANSLATE_API_PRIVATE_KEY_ID
30
+ # # * GOOGLE_TRANSLATE_API_PRIVATE_KEY (be sure that it contains correct line breaks)
31
+ # # * GOOGLE_TRANSLATE_API_CLIENT_EMAIL
32
+ # # * GOOGLE_TRANSLATE_API_CLIENT_ID
33
+ # #
34
+ # # Alternatively, the contents of that file can be given as a Ruby hash
35
+ # # and passed like the following (be careful to use secrets or something
36
+ # # that prevents exposing private credentials):
37
+ #
38
+ # Lit::CloudTranslation.configure do |config|
39
+ # config.keyfile_hash = {
40
+ # 'type' => 'service_account',
41
+ # 'project_id' => 'foo',
42
+ # 'private_key_id' => 'keyid',
43
+ # ... # see link above for reference
44
+ # }
45
+ # end
46
+ class Google < Base
47
+ def translate(text:, from: nil, to:, **opts)
48
+ @client ||=
49
+ ::Google::Cloud::Translate.new(project_id: config.keyfile_hash['project_id'],
50
+ credentials: config.keyfile_hash)
51
+ result = @client.translate(sanitize_text(text), from: from, to: to, **opts)
52
+ unsanitize_text(
53
+ case result
54
+ when ::Google::Cloud::Translate::Translation then result.text
55
+ when Array then result.map(&:text)
56
+ end
57
+ )
58
+ rescue Signet::AuthorizationError => e
59
+ error_description =
60
+ 'Google credentials error: ' + # rubocop:disable Style/RescueModifier
61
+ JSON.parse(e.response.body)['error_description'] rescue 'Unknown error'
62
+ raise ::Lit::CloudTranslation::TranslationError, error_description,
63
+ cause: e
64
+ rescue ::Google::Cloud::Error => e
65
+ raise ::Lit::CloudTranslation::TranslationError, e.message, cause: e
66
+ end
67
+
68
+ private
69
+
70
+ def default_config
71
+ if ENV['GOOGLE_TRANSLATE_API_KEYFILE'].blank?
72
+ env_keys = ENV.keys.grep(/\AGOOGLE_TRANSLATE_API_/)
73
+ keyfile_keys = env_keys.map(&:downcase).map { |k| k.gsub('google_translate_api_', '') }
74
+ keyfile_key_to_env_key_mapping = keyfile_keys.zip(env_keys).to_h
75
+ return {
76
+ keyfile_hash: keyfile_key_to_env_key_mapping.transform_values do |env_key|
77
+ ENV[env_key]
78
+ end
79
+ }
80
+ end
81
+ { keyfile_hash: JSON.parse(File.read(ENV['GOOGLE_TRANSLATE_API_KEYFILE'])) }
82
+ rescue JSON::ParserError
83
+ raise
84
+ rescue Errno::ENOENT
85
+ { keyfile_hash: nil }
86
+ end
87
+
88
+ def require_config!
89
+ return if config.keyfile_hash.present?
90
+ raise 'GOOGLE_TRANSLATE_API_KEYFILE env or `config.keyfile_hash` not given'
91
+ end
92
+
93
+ def sanitize_text(text_or_array)
94
+ case text_or_array
95
+ when String
96
+ text_or_array.gsub(/%{(.+?)}/, '<code>__LIT__\1__LIT__</code>')
97
+ when Array
98
+ text_or_array.map { |s| sanitize_text(s) }
99
+ when nil
100
+ ''
101
+ else
102
+ raise TypeError
103
+ end
104
+ end
105
+
106
+ def unsanitize_text(text_or_array)
107
+ case text_or_array
108
+ when String
109
+ text_or_array.gsub(/<code>__LIT__(.+?)__LIT__<\/code>/, '%{\1}')
110
+ when Array
111
+ text_or_array.map { |s| unsanitize_text(s) }
112
+ else
113
+ raise TypeError
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'net/http'
5
+
6
+ module Lit::CloudTranslation::Providers
7
+ # Yandex Translate API provider for Lit translation suggestions.
8
+ #
9
+ # Configuration:
10
+ #
11
+ # require 'lit/cloud_translation/providers/yandex'
12
+ #
13
+ # Lit::CloudTranslation.provider = Lit::CloudTranslation::Providers::Yandeex
14
+ #
15
+ # # API key can be given via ENV['YANDEX_TRANSLATE_API_KEY'].
16
+ # #
17
+ # # Alternatively, it can be set programmatically after setting provider:
18
+ #
19
+ # Lit::CloudTranslation.configure do |config|
20
+ # config.api_key = 'the_api_key'
21
+ # end
22
+ class Yandex < Base
23
+ def translate(text:, from: nil, to:, **opts) # rubocop:disable Metrics/MethodLength, Metrics/LineLength
24
+ # puts "api key is: #{config.api_key}"
25
+ # puts "translating #{text} from #{from} to #{to}"
26
+ uri = URI('https://translate.yandex.net/api/v1.5/tr.json/translate')
27
+ params = {
28
+ key: config.api_key,
29
+ text: sanitize_text(text),
30
+ lang: [from, to].compact.join('-'),
31
+ format: opts[:format],
32
+ options: opts[:options]
33
+ }.compact
34
+ uri.query = URI.encode_www_form(params)
35
+ res = Net::HTTP.get_response(uri)
36
+
37
+ unsanitize_text(
38
+ case res
39
+ when Net::HTTPOK
40
+ translations = JSON.parse(res.body)['text']
41
+ translations.size == 1 ? translations.first : translations
42
+ else
43
+ raise ::Lit::CloudTranslation::TranslationError,
44
+ (JSON.parse(res.body)['message'] rescue "Unknown error: #{res.body}") # rubocop:disable Style/RescueModifier, Metrics/LineLength
45
+ end
46
+ )
47
+ end
48
+
49
+ private
50
+
51
+ def default_config
52
+ { api_key: ENV['YANDEX_TRANSLATE_API_KEY'] }
53
+ end
54
+
55
+ def require_config!
56
+ return if config.api_key.present?
57
+ raise 'YANDEX_TRANSLATE_API_KEY env or `config.api_key` not given'
58
+ end
59
+
60
+ def sanitize_text(text_or_array)
61
+ case text_or_array
62
+ when String
63
+ text_or_array.gsub(/%{(.+?)}/, '%{_LIT_\1_LIT_}')
64
+ when Array
65
+ text_or_array.map { |s| sanitize_text(s) }
66
+ when nil
67
+ ''
68
+ else
69
+ raise TypeError
70
+ end
71
+ end
72
+
73
+ def unsanitize_text(text_or_array)
74
+ case text_or_array
75
+ when String
76
+ text_or_array.gsub(/%{_LIT_(.+?)_LIT_}/, '%{\1}')
77
+ when Array
78
+ text_or_array.map { |s| unsanitize_text(s) }
79
+ else
80
+ raise TypeError
81
+ end
82
+ end
83
+ end
84
+ end
data/lib/lit/engine.rb CHANGED
@@ -9,11 +9,11 @@ module Lit
9
9
  initializer 'lit.assets.precompile' do |app|
10
10
  app.config.assets.precompile += %w[lit/application.css lit/application.js]
11
11
  app.config.assets.precompile += %w[lit/lit_frontend.css lit/lit_frontend.js]
12
- # add language flags to list of precompiled assets
13
- if app.config.i18n.available_locales
14
- app.config.i18n.available_locales.each do |l|
15
- app.config.assets.precompile << "lit/famfamfam_flags/#{l.to_s[0,2]}.png"
16
- end
12
+ end
13
+
14
+ initializer 'lit.reloader' do |app|
15
+ config.to_prepare do
16
+ Lit.loader.cache.reset_local_cache if Lit.loader
17
17
  end
18
18
  end
19
19
 
@@ -24,5 +24,11 @@ module Lit
24
24
  end
25
25
  end
26
26
  end
27
+
28
+ initializer :append_before_action do
29
+ ActionController::Base.send :before_action do
30
+ Thread.current[:lit_thread_cache] = {}
31
+ end
32
+ end
27
33
  end
28
34
  end
data/lib/lit/export.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'csv'
2
+ require 'lit/services/localization_keys_to_hash_service'
2
3
 
3
4
  module Lit
4
5
  class Export
@@ -17,7 +18,7 @@ module Lit
17
18
 
18
19
  case format
19
20
  when :yaml
20
- exported_keys = nested_string_keys_to_hash(db_localizations)
21
+ exported_keys = Lit::LocalizationKeysToHashService.call(db_localizations)
21
22
  exported_keys.to_yaml
22
23
  when :csv
23
24
  relevant_locales = locale_keys.presence || I18n.available_locales.map(&:to_s)
@@ -55,23 +56,6 @@ module Lit
55
56
  end
56
57
  end
57
58
 
58
- private_class_method def self.nested_string_keys_to_hash(db_localizations)
59
- # http://subtech.g.hatena.ne.jp/cho45/20061122
60
- deep_proc = proc do |_k, s, o|
61
- if s.is_a?(Hash) && o.is_a?(Hash)
62
- next s.merge(o, &deep_proc)
63
- end
64
- next o
65
- end
66
- nested_keys = {}
67
- db_localizations.sort.each do |k, v|
68
- key_parts = k.to_s.split('.')
69
- converted = key_parts.reverse.reduce(v) { |a, n| { n => a } }
70
- nested_keys.merge!(converted, &deep_proc)
71
- end
72
- nested_keys
73
- end
74
-
75
59
  # This is like Array#transpose but ignores size differences between inner arrays.
76
60
  private_class_method def self.transpose(matrix)
77
61
  maxlen = matrix.max { |x| x.length }.length
@@ -69,10 +69,20 @@ module Lit
69
69
 
70
70
  parts = I18n.normalize_keys(locale, key, scope, options[:separator])
71
71
  key_with_locale = parts.join('.')
72
+
73
+ # we might want to return content later, but we first need to check if it's in cache.
74
+ # it's important to rememver, that accessing non-existen key modifies cache by creating one
75
+ had_key = @cache.has_key?(key_with_locale)
76
+
72
77
  # check in cache or in simple backend
73
78
  content = @cache[key_with_locale] || super
79
+
80
+ # return if content is in cache - it CAN be `nil`
81
+ return content if had_key && !options[:default]
82
+
74
83
  return content if parts.size <= 1
75
- if content.nil? && should_cache?(key_with_locale, options)
84
+
85
+ if content.nil? && should_cache?(key_with_locale, options, had_key)
76
86
  new_content = @cache.init_key_with_value(key_with_locale, content)
77
87
  content = new_content if content.nil? # Content can change when Lit.humanize is true for example
78
88
  # so there is no content in cache - it might not be if ie. we're doing
@@ -106,7 +116,7 @@ module Lit
106
116
  # it anyway if we return nil, but then it will wrap it also in
107
117
  # translation_missing span.
108
118
  # Humanizing key should be last resort
109
- if content.nil? && Lit.humanize_key && key.match(Lit.humanize_key_ignored).nil?
119
+ if content.nil? && Lit.humanize_key && Lit.humanize_key_ignored.match(key).nil?
110
120
  content = key.to_s.split('.').last.humanize
111
121
  if content.present?
112
122
  @cache[key_with_locale] = content
@@ -129,7 +139,7 @@ module Lit
129
139
  # end
130
140
  elsif data.respond_to?(:to_str) || data.is_a?(Array)
131
141
  key = ([locale] + scope).join('.')
132
- return if startup_process && @cache.keys.member?(key) && Lit.ignore_yaml_on_startup
142
+ return if startup_process && Lit.ignore_yaml_on_startup && (Thread.current[:lit_cache_keys] || @cache.keys).member?(key)
133
143
  @cache.update_locale(key, data, data.is_a?(Array), startup_process)
134
144
  elsif data.nil?
135
145
  return if startup_process
@@ -139,11 +149,13 @@ module Lit
139
149
  end
140
150
 
141
151
  def load_translations_to_cache
152
+ Thread.current[:lit_cache_keys] = @cache.keys
142
153
  ActiveRecord::Base.transaction do
143
154
  (@translations || {}).each do |locale, data|
144
155
  store_item(locale, data, [], true) if valid_locale?(locale)
145
156
  end
146
157
  end
158
+ Thread.current[:lit_cache_keys] = nil
147
159
  end
148
160
 
149
161
  def init_translations
@@ -155,7 +167,7 @@ module Lit
155
167
  # load translations from database to cache
156
168
  @cache.load_all_translations
157
169
  # load translations from @translations to cache
158
- load_translations_to_cache
170
+ load_translations_to_cache unless Lit.ignore_yaml_on_startup
159
171
  @initialized = true
160
172
  end
161
173
 
@@ -179,9 +191,10 @@ module Lit
179
191
  Lit.ignored_keys.any?{ |k| key_without_locale.start_with?(k) }
180
192
  end
181
193
 
182
- def should_cache?(key_with_locale, options)
183
- if @cache.has_key?(key_with_locale)
184
- return false unless options[:default]
194
+ # checks if should cache. `had_key` is passed, as once cache has been accesed, it's already modified and key exists
195
+ def should_cache?(key_with_locale, options, had_key)
196
+ if had_key
197
+ return false unless options[:default] && !options[:default].is_a?(Array)
185
198
  end
186
199
 
187
200
  _, key_without_locale = ::Lit::Cache.split_key(key_with_locale)