simple_po_parser-1.1.6/0000755000004100000410000000000014217570357015125 5ustar www-datawww-datasimple_po_parser-1.1.6/.travis.yml0000644000004100000410000000020114217570357017227 0ustar www-datawww-databundler_args: --without development language: ruby sudo: false rvm: - 2.1.9 - 2.2.6 - 2.3.1 script: bundle exec rspec spec simple_po_parser-1.1.6/test/0000755000004100000410000000000014217570357016104 5ustar www-datawww-datasimple_po_parser-1.1.6/test/benchmark.po0000644000004100000410000012077714217570357020414 0ustar www-datawww-data# PO benchmark file header # #, fuzzy msgid "" msgstr "" "Project-Id-Version: somehwat master\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: last t \n" "Language-Team: team\n" "Content-Type: text/plain; charset=UTF-8\n" "MIME-Version: 1.0\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: m+h+H6l+o/zMYZ3FIGrCJRLOnwEpuIB7Bv+BmU0R/Q== msgctxt "AEli7osWHSRGFtzs0IqkZaigqPyaZwuDhxXdeQ2KY5oVelVnhgzu1bJGSMM" msgid "QtkIdWTljoOSLjOfPQismPI/IulEDxDq2oOpx1A2f05x2lnX6UdzJ/61QikXluSw5UnaRmwT2PevqzbZNlqElmHemnbAGeyD7Ixd6MryovaeBIIB/60Y1lKzH8NTm/L/Ts1Y3n0FLkISXwhqHKukSSCKmLJFjXRl" msgid_plural "yrt9qstDmENB3jiezA==" msgstr[0] "/zry7XukzOFCxC5uYeFf929pTCFhAWJFWp2OB1Bm2JhSb5M7uLcshXZY1mGiE6C1n2en5tNPyHfA/8hxr0spfQ9g" msgstr[1] "/zry7XukzOFCxC5uYeFf929pTCFhAWJFWp2OB1Bm2JhSb5M7uLcshXZY1mGiE6C1n2en5tNPyHfA/8hxr0spfQ9g" #: H6gVO/bfxh5UCRDw2Xz9sIyNo5oRxwrQJvU3MgnZ msgctxt "yWjZcO+zwP80XHIOd5LY2P0lGc5XH/Nf3FJWJVJdqTWb2O6wzVY9zqkNMNveu0szMNUW5s2vtD19RQ1G" msgid "5iMGZrIjcXPPnjQCmmIk2uOgrGa5iWNmYZgCSfw9XJEV6wLRX8CzGxzlZNYn2BzYKmQLyieZnKrzRhU1brN2w26odcwCgnZKu3klOJ2yEOJtnRC/E40zC6+t3Shu0OGF+I5fwnODFcN4nZ2nVg==" msgstr "Jpr5bTW6QBjLosltbBieyObSWZnkyZ+cfLlb9VSdXHNLS6HRWA==" #: aSD+youWGbj4dN1ZQI1XTjvhgj4cwTaA8vVQljMusoemN3lU0raoQQ== msgctxt "SoPybmFEnpJ3C5frTQ==" msgid "N5V7svBB0zMhJarlCDFOpBzLXt8LOx3vHbgNwLmIB0HzC5MfuoC07Z/HdoN4g7HhuS9jCWircg==" msgstr "uOl/2r0KSBNQ5dQ/L3lzJ3q/pb0sOj6MVAjhz3YaiUz/9GazgWMpX8zo7d96MwxucYXZFIfhTtSfHZCURB2wFRgAB2bxl29GnJzHOtjudWjKKtDK0Y/PPDwmkneT/lKH+kJ7pcbq5UGoAGFcm2a3OCCuGwRqCwWpGflly54K4CCxbKfzcA==" #: JrjroYo5lA== #: h1Y4it53IhP/WZvz3iVjaE6bKumuvSjQlsdMMZr2fJpwPbxKybkJKoOgeXOb #: DfiJuPy2dDZxteKOvjj+C+gbkt5j9i1/xtOSsymhpgDC4qIB/UFARrk1b9Doivy76A== #: LCZYHOHxZSCqj5VJcUEAOgtJpctcUfMxWvkc61X4x5GXRMB330mDlKO6xYQ= #: msgctxt "86vV6CdxDgiQJOom1F8P8tMJJKz/9o54j7LEGyPDPz0ZtP9q" msgid "mw/fkjvMpIG18ZrmL4kVbZaeK5nldXaIycd+1k0aAi81aJzen/Bkq7/epV1DH09LcqT9vqR0gNAgLbi9lLH/h+MPHL5b4zo=" msgstr "JGzpNzm1QZ6Mb9bYsR+eEeLic3H0SwnWXQSyc6nbFuYsQ07NqjUvr+STtFwrWHvwkrs4U1FCJlcoG4F7trg8ZJM=" #: kZ7OVSwllBbIbmpXP+85Bhc12jj05j4= msgctxt "h65Z0/lfDC+V5EYgvG6IJJ4fTbUwGnWhBdz+q5ea6Q5THeFG1FJEuhlsAy9+Q9foGLE=" msgid "wLiriIDgX5Hd6ThjK2GCNxz7h81GYrZdA4HyYZjroa0TYjhDFUQYSXvlan3IJr7uEn9xKcvLqp0C3Zu8TTMUMOy9VtJlVVdspJEv30mnIY7OmQ9gh/SEkK/o/Tcyy+kcRF5Lgyh1coQjTsoI7HkKfA==" msgstr "xb+yJHpjQ4Yujn5iQzIAxfb/5w==" #: EnthqvkNLwUiVfYEqaoyTq60uJgdlaOTeU2qf6kF msgctxt "9ihYCwlIEg==" msgid "AloClNt81srB2Op/gEoKap7QvzlMzWBXxMEWLyjI2bQZV3praFAT2jpYhfQmgzu4Wix2tAsOeZV6lZ1TcnRmuHXht8FLfE016Q==" msgstr "i+9eqs/otjcsICeBK5MEcfQ5hJOmP++ROezIMcPEWVOEh+XAT1brUoxfqrQAMefK" #: jg4iou6yKnWxlkOF5Hw50gBFXZuUaBRTCJ7XNHeEqikz msgctxt "9dPrCWJHJu9pkv7ioE0gmE563vhWJwoO" msgid "" "ZP4A+/XWGwER2cE=\n" "llqjEdZBpEpLdx72tT0I3M38kqWVwJnnkxxL6nJslKC6es+nfci4\n" msgstr "" "sKNQ7Be/6PUKvxMOEOVGtLIRFByih6tvt47hxGW0EqLU78FZMAbx7u6RBUZvghqjG6c=\n" "BwyW73h+yBRohS0dzkg1GQ7f/oWOLKdA2lIDYjYrhWu7BNpb7yeu4ZPUlLvswTLI8mKmGqOUxQ==\n" #: MH2Nksc= #: 5M/f74zFJJTCW0A1/AZBW2EuESg3ZvnRzMkOP0kBJ+OtRTdk7w== #: 7QJjYuJkkqmim7AYvw== #: qMi3sM3C9kNHnGxUN+AznU5Gogo1JkWOnhhpW1jtG7F/UQ17 msgctxt "yllw6Az6BJFY9pFcI3Iu0S+wlkE2W+m8RKb/T/zfYT07cKB+wqrROZGKyop9ghQ=" msgid "OcX+h/Sm7Bd3dzrOE/7TMWZuFo9PuOyIMCcpeTnvp7xqnQ+nwNduH76FoTUXFbs+Bp9QWpfILppFxhKLo4NQgOrn/a7kZ4TeDDJBc5OO2x/Ruvfi4/ZOVhIGhWJNcNsgeBGbo7zrJ1dp40EDvflwQdNTyV9wA5HwQCrXXdwnAwO3ZRIWvA==" msgstr "qnGx/OS7MCSaIQafZY0ouNaGhxjSMdHIIBervBHxXr5KDSNcYMiEOtTZ9s2ubuk8s0JqRNCuxZXtfedVzMMd2f+7IIKLfu38Krun+8Wh44jgOkHF7c8Z+3dTOsP3+6eJOA==" #: er0+DIITT1z9VNOWNQ0= msgctxt "f3VqFzn9TODhG6w=" msgid "h/HRX1b5kUJPMJQ0VnoIlbqLjjRGxU9oaaXkcMxC2jrSlSmbWEJy7hCmzoxftdm/M6pRYYWE5JSCglv5SKDCaCx4Pw9PbBc3KDe9QOREEupCfcC4JlA3Bm9TFTEAGs5rPpg5HrsB7gMY0pBSltFBIJe+LQEYtKtFLiSHg8pkl9lfFD/1NJpMjQ==" msgstr "j3ntorS13hLFvoFIGPzPjBS5oz4yMW1PhcnOzu0yk8DUYaudGlgo2Ggt0M/6EwEDFtaHzyLpGHduFZybH8JamqrLjAlI6W1GmHVpxIS9CGPepsN/nlJw" #: r2+DT8AHAaoqJacYykjZZzQXct8Gmw== #: AJyA93ZRj+1V7+xyZeWLFkqdPNGfgYG6/TaHbtCf #: sGNU9rpwHacu4rjaHnCvM3+fCnVypsFZlZvoPy1PTiNY #: oRITXeQh9W112xNTSQ/cnkzGPhbBkiaLaLhSqTu0IzjWweU9GBgfBaA= #: /2QiHyb3yR6gn0ipIiNoJXFgaSv9dPEeagzvGcWUPAaYAAboFUb4ZAzdNh1e msgctxt "fIuWrJdsJ5oTwkG9tckouFrtUU9ceKe0wtY2qUpOGHyGT0qHZnI00OUM1x5H3wM8XflgEPGIqZWbAg==" msgid "kQoSwDQ76dGK6G1dqFEkE618nsPOwVCtQ5mh0m4DeNCs873riRFwCqo0K1yKFXYy1z0wJSTft7YSlJN05m+MIo98x81W3VDSZLvS44+IVUqrsIT2HbPIOtnADq8Cyq27z5G7kcfR5rPloOTCfLruJ4KyuDjuZs7hHw==" msgstr "MKDopDznYZB0I8k3DcjAjJcF4aPjEDg/utJJqAnXvjT9PBSvF6j2b+AfegrWGL5+XmQZuy4/eDv+RzzsZP/ilES4mmcK6Lx7+6InzwVjL2H2glr9R50fHmch9Jru5qtT+/s=" # FsIREQ6GmeuuEEZRnvRPUrXKnwfYqKm3 #: 4V9RQPWuJ/yQU7tUY6UNwV/Icg== #: oH1gNGhxawtCS8lStY+8HWxdr1o1FbeFGZ8N0RGLuFMzL5W91F34SEUtkn4= #: T8vFX0jPQZrnpd+i7GO6foNYfzDSBftKoRkG msgctxt "LlwD" msgid "EaDWzP75TqTaTlxllI9sUcLG35qi2yUs098RCJshJPpZX2H7Xq5B9oitC4K4WeLXnH+YjbQMBj/5xnx/A8TJt7wS00s1S5bt7tAZvsT1wlIPTcqzF3kbcZlr2KxsNLYtWlAl/kprM26/mBD7Wl8SqertBRYb9J4UxmgR7jh5AbXsr7FAHmaDRYcK2lo=" msgstr "Kc1i3RBhSUmvxFhHFiUhFzj5THt8SlfM" #: A+BM8WwEzij/Ic+s msgctxt "nA6v" msgid "3c5Rq1GsjWVJpiAdrFXuwPpPhMxezuRGufJgfy7v9Qg+qxPqxajOggUXkDpoByJfHbDcl/2o/ekpdzZcjHUq/A==" msgstr "o6CJxxELvCx0BTed28R4cQfgNrw5ZbAdHmSb0Igm6zxiBoRZ84RMsUM3U1Edl9bn+5pU+4DPOa56kMwd99M=" # ko0C8GP3GSfgkYPkqYbtO7CZ7r4h4wTQDWoBrzDP #: UBtoNWKSXrONTdtsvF1n02Iu20V+QUXb9O75cA== msgctxt "NEvWfLxim0Fnwjh6J/l0PHuZ7nnijBzBWOvqqW2lfLsTAmV79OhjKOovYKJY3T+V94cys5BeGiYw/N+nSZZz1Sy37B7z" msgid "" "d1WC+kZdA+D0jVisnx+ymnLh9LR+jjmA1ZBZpGE=\n" "Hcy5hG46Pw5LagQkiGM/L2vyOY+8OistQm4txtlEKgANKsYTIoNnG6BLeqMm4Q==\n" "ofroefaklot4zV0wFUEjfFDw8g0Qewg=\n" msgstr "" "mU7Ueamc4c/AyXVDBS+J\n" "yJ5kTojgFaI/cu+0nX75g3PJtbjN2iZwI8fOnhbMUzsQgSibY3ptjYm8USvZrA==\n" "EtOkGXeeJUcdZ0s7Ct0boV36/wkPvhixOGcKPMb9sdV47FVNouROrajL1vNAoucg\n" msgctxt "z/Q344KKdA==" msgid "oRzH51UpKW7g+XGe0lg7W6JupXCceNeKV2VxuREUhveJD+a/0GnyhfKpCdqRnxvSN71476b3f09fDfo6KHrVCRB+dxNZ29XmfFy71my8VaI6tbw=" msgstr "ywlF4ijXBLqXN6V7mzMklik/28CYOa7X0CJY62F8nhq+kreBMIAFnQLyrk0/Gke6VhN/DgvCPJKmFQdt9Nz79GOqxpmkXc/QnhGj60PpCYxMgP1rBiDKzfWRVjikPHfrdMH/RDlmGQ==" #: uoY5cWc= msgctxt "GW6HhhqPAGtGIoTFKH1x1ukHrqwY58qbQhlOyyZAb6FRcz2Eo2PsIIDPYg==" msgid "" "m2fR1pNEdWvpfAOzfrPzpHoJg6LT6oGcbicp5lxwtPhZBSf6BVv71wZL4A==\n" "9/+fq1KCrIOjEeCewc1uUGJp0hq4f4afRwNsbie/vW/Mbd+wXIpeSToA9tC7pQ/NkCTLrT86iTyv\n" "tLST7E9yOGENqTbUKwCpkttP5xTlxY44szJ81Q1YLfMJKOYp\n" "qMUOqTE0m8rMpw65q7G8jtKTOtCXinHK1YLrNKML2nLpIyPFoPqkzOShJxivKQeyqRPBFXtN1mhi+U+cfl09J9kF0TNL\n" "LJM2\n" msgstr "" "WdeY5Dg=\n" "SKZCMv0OfyEG+/fnOCanWNJQG/VlXaZ8AEP8kMwdjElKAikkeJbqHjF6AkPI+HBdn1eXU31iDoXTPYE=\n" "I7/geEX7KzOANQOg5Jbycc8/n5w8ytVVIGb0MXkAHlEPeqMUmxYManuk1NqOFscxn/ObUK8wZRXUBu8GcBM=\n" "eZncBSVvNOUWLxI99UIa2wfrwpkNBBe36auToywVpoc8jE+pVXdG6gmhYx5AGV35x0hdUg==\n" "7dWKqfpjvwoElyseHz2iVdOF07AEd+2EoaztqBdzJOzVRS5e3JhxXcPoMw==\n" #: F9DAFH+Ew8kh3bpXprtJ6HU= msgctxt "A1WDJiSQ4m8auFfuWs9AJiSK" msgid "VklPIdSp3LaCnT1k/ZZ5s8IUS8jvAIRjlliWDOWXiXZMiA==" msgid_plural "6/ddSF4GYSGJmWmrbpxuB6AnwG1WATc2UXlorB76a8Kvqs/JCN4UR2AOVEBgVdbPl1635kv5FsxewguUkMPH9sv3j+t7i3m4QmZluJTx0db2rRJcwY2QYhiPalWtR8IddSeQsVd0NV2m0NNpPCwITAyoGKY=" msgstr[0] "G2QscJ5Q3W2vAHnyHVCHy9RIFNaCLg9DTVr3Dv3CPfgEMBTuKAdZCOxP5Te1A9vW" msgstr[1] "G2QscJ5Q3W2vAHnyHVCHy9RIFNaCLg9DTVr3Dv3CPfgEMBTuKAdZCOxP5Te1A9vW" #: omc9f8DBncegJOK5zW/L+fjquVG07L3GG3HVaQ== msgid "00xLwPsVgSH8jLCn/YtqasW3PDizVciQVv6FVHq2xhbAAUK9c1U/RIlmHosVToC1b0nPpvy98rUn4X1Y0b2/fFqtZ+dKBQku7XK5O56zYiUoZgUCr5O+8U+XTsu3lPFgMjDaEpl3bw9n01KNJ3andk6XdeFBOGJ0/ADvQC+7Z0a17gSdow==" msgid_plural "hFmonbo4pdSMhtLvVZ81PHV7ztyJ3pMLLZQKRzA+4E6NRD4zSEpI7ZAm7581dIAE8D536EdUGSssF8UaJDNUM63hVT83C6sx5Yo4Xfa7LsViZjeLqGFgdMosohFqLP/qQeS6DzRhp/WwtJ6bjZ1/ZFOQJ4kQ1FVJdqgJVF24hoMjMP0z" msgstr[0] "v0xhFig0flhYswpPfXRuyfHqL4ilFrq614foWCimn84EMbrqlskW83+vdQmvoM81CpjGyXs14x7Gog/g3MbLMscqvQI43wBzdx5OrmBrtkBaBPCUhs1ivZpzuOgTF3tUQSdmhuqL1VevreadVJjUVlX2ulSy0R+Nk+gt4PDeJpq7Y7iR1v5dvIEpYHgZiOj1" msgstr[1] "v0xhFig0flhYswpPfXRuyfHqL4ilFrq614foWCimn84EMbrqlskW83+vdQmvoM81CpjGyXs14x7Gog/g3MbLMscqvQI43wBzdx5OrmBrtkBaBPCUhs1ivZpzuOgTF3tUQSdmhuqL1VevreadVJjUVlX2ulSy0R+Nk+gt4PDeJpq7Y7iR1v5dvIEpYHgZiOj1" # 4BxJN8D2d0xZNy80odgZcubVkvJx4MZgWX+Y1bnwad+IXMHeSKNYDQwwhQ== #. Yg== #: Me0L1qmKXj969EKqMfTno0boh+kYfyCEgO2qdw== msgctxt "gZ7Cz7B5Yka0rzG7SZDhGV+hVFfmGfA97r7AIrfEFOcv0E+0hub/zNezQQ==" msgid "jvVE30uDPlGrP8EO3uL4M47HfS6DIBILGhjIiaxI/kAYZ8QWWeTYpF/AyHRwcBCwrXeof+zd4AFtUOpS6mkJ2FoXnNa6hBhkM+0G3o19U/MVC06egiOoPj1Rp19YJ+Wrc5lpQtaNQRdkz5/13V6adOU=" msgstr "JcOB06OlFxP36kMvbpe5S0WKPdFHmkDeK66uyBkF8HTixJuRWcXt4raLQjISlKDFzVbOdR3OYZHUuMvmK9J+PZuJ6FyP8bYoEjsSYFnT51R3itE7+9fzCAAJoZ6vvDKwYgKjktGujBCXEPLSgkk=" #: jAOjaZJQfKa7E0MYZEusbJvs+Mbk5q7Jr5XJPlLAWzKjmQOvknjt msgid "" "xec4VRPLmWbCpouWAG4x\n" "IqtQST1aqktHTMAms3IMfOAM22RzMOrjQntng5dGF+ah9TVedNddf0ECYxpvMuphpEYE9msF+jwaMlTm3f+GsqArTBg=\n" "QCevcHTPmHeqoMSyZTlLgNd9gmQsjSEVI/iJ3mUq0Pp52xAo/d6DMR/n8wyL3NZ7wHcAAVAd\n" "//E6O4Zd6B41MeJzUtceTtJI2a6w3YOWAdBlUkO5y55ALmnkYVHEc55GPThCmA==\n" "gZykQfM=\n" msgstr "" "LYgAQPrAsh1U1gtK1IXUzxw+3/KiDPrFrZYIVUrYU5i1zhCPUjJaCxzPmuzZmEvIPp0RVrKPnB1yb7E=\n" "yYIFbVdRQhUgW4e0OC3T6sql3ZGomN71F8YtTPQ31kL71Hz44w0KcN356itqfytH5Q==\n" "K8EBUEhQiuKXhVf8SqhbqiWavhV/DL1fKPRX5HJCIOZmEb+SJ89Rou0GMdr3\n" "jGwSH+MSgbcrgZSBTS4ODCysagfY2wQW37Y=\n" "yuDTLQ8EeSADJwkYarUPtQ==\n" #: /YM0mzd+DHy7pzRp8LiFgtFOdOInm0coj2f0slhdTXahMv+9eEXuDvl3 msgid "" "u5XDqaPKqXgoDRorNSWRqXqVHmLhUlCVUq4xQljdG95xjdxjGuUH9wIIzVhKqqi8\n" "R0yYbJmaVzEj00k659iP+R/f9l7k29qC4SAKvQ0FJ+IpdH3NkT76+Yzs8uJu1cLk9n7GsZc=\n" msgstr "" "Dg==\n" "FvJ+zE5odXB2r5OSlXkKT2l8NLd5APHEE8DzYIFs4W8vimS2588dmGZV1Wt3BbhzTHQNULdtI1lFMd7smw==\n" #: YJbn5MrPiw== msgctxt "tulXYxrUOIKP28lzFVmwWyMoEJ5qtifSRJerUBxHhdSNzJGYFoUNchsPohUz7XgbG19tGM/PPuRqdwA=" msgid "9XdITszHCaQcG2V1ozY84NUHIJV1SJppnDmEmJucjp4PqO4hvK/6LfpHoXD6Ty3Vv0ul+OAgEi4X7+Y/sO1WYbzeVAh5QKqVrdJMaob0g2mTBVxJB/f8h+6u5DLRVUuLjW32K+lkTNzDty16ElXtlnIvmns4PHDjmhCj26Li" msgstr "WMuC9/EliSWHmn+0O8+moDiK/wDDk2wYuKBkNR8tyVnjRQqS+1h1qYueX4TaeNyKnyTlrH8/gJRwRqZI8mAL64LALqWc4YP/DAyPTxwqshVzVpV+d2Dr9ImuKAmqux0vAtGqa94dt0VF/QGSNSCuM9ZLxg==" #: rWp112wGWYkIRAFOkds/J/XRMqWgE8GyM6qmtFRR msgctxt "094CwanGlFvGHaWgdO++etQWWxqCpZrKSvsrJb5Y9UKt1oE3" msgid "xC8FzBjBZV0mdudhDfuzgIjRdfH5POWHxN0C8u9X6TW326t79Lf2fmhsxLUR1rNGmc/a9b1ICZhyoRsrpMVhaalk+JZtGFMPfwwdJI/vzPwL" msgstr "FwI0qOY9ntkesmN7OVn2Ls2zKJsi/uOO2oGjhg==" #. ScA2De4a/31qTRIByA== #: kfHvIyB1cKX/osgk5vX6SG+URoziPCl+tPZBgURD1ymfeh9Owt/w msgctxt "qkdpxmLbmdE8uk5026Wfk3+BGdoE25lwnp0ka3SLfHFx" msgid "r4HcYmUTiOU2h4eL0J1GFNsPqS0PdJ0=" msgstr "vTJcbHaBk6Z7G1TQcyHsDuQ=" # qEcHBMQKiT2wqzlXX1jQPaQDQgRYgGcbyJkxV3sBxRWebkYXwftcKtFyE30= msgctxt "8Gl6N+JZ9KkBiwLjOssqKsptv2nvyohNFnyn31KfMzhQXfYDH7mLNAOvd35Gp0FN0xyOdIOZsFehgTRbyA==" msgid "rVqqNBOUG5hBWM+XWckqDOyYXRNTox6M1xm5jdJQJoegg/1GnVqWltoIXurOucfnIu0Z9wH3zLNx/nB5mW/BkvbZ9PJl0rIA5KhRD5PolF449jrrorygvOeACXF3ma5tXY+prEsdmIKubc5nqXfVnIAxPsPxI9ubRX1s" msgstr "2n+iUL8mGdOmuGWaPFYEKQjs" # #: Hs3jLKTiTeHeRyCnYL7/hnzlpm9zXgtU+qi+qE6JSplkQg== msgctxt "J3FqLpkySYs=" msgid "hVatPW31M8ycJkYTD7sZviA6scSS6UqeMDgF3MS9Zx2wXSuSrriSlQEbQb9Z3TCgwpcfQYowbErMvUex3lEKvgKmpElrIBv7LHIxoEjDr18gMG63cd9ZKcGtVH/r+HbabLe6HHIgyz/u0T0jCBe5v1qzr7las/o7VTcWiSYANGy5LfEjGWC0wfSU3QPJCeC/p2k=" msgstr "oSzDbUeDJ91OmNWInA5jKvWMEsXrQHLrcFTrCILkHodAi+Z+J+9UHtvOJz8y3EUNHlTzpGO/oslPzVIBewdzD6NFTJHphhTiSJo8IMoenF+eTw5i/ZF6D1psqmFECY0i9KeFsn7PGOOYSHb0pQhIb38=" #: CDT9SNbvO1UqvK4zepP2DSZh014ida09/CzJ32mnYFKDpQ== msgctxt "5s+YJUI3oR3ks8E9J+Jw96UGVJWeTbMxYt9Pmwr88fA4VpU1" msgid "+dNPLRODNoh+bEfurnrfZyW12ztIXFTt+CMsdQDUSBVUvVbPlJyhdPDSXOQhHipeA4wCPpAOaAS0k5gxFGl10o9OapUmeaq9PjE=" msgstr "GuLEnnavKW5BGn78o8NjJX9Pqq/Uk7Yt7kcErHu4GnV5VpW/jgx+tsZHJ3uA/TURs7Gzbg8Wmhn4H1abQtg=" #: amLa7A1OJ2aEV4vBDfpWFFg9s3M7gLNkLUXglJK3KNbE msgctxt "fL3MmMTNFUaUU3hYkZaonW01njtGyL9F8bMFLaNiMUBgi190zILvO3oCM7nWSq8hvyJHZ5QaGLmyhXQ=" msgid "VxPWBFQrKc6wgrHyiQv8aDlLw6SKmQ5glBd2dHe1BXfOvc7VmKs02VqVLx/RyYFlbBflae+guM6m+GjXND+saOwDxE7A6FacOjpvXqqZAcAsr+JWbogR5v23nKNia38ZcaCW9y2YhPC8ltBvp9ug+XsenBmgUiloFf5veDPNDpsLDLUJVQ14+CzIWtr2" msgstr "QdHYGMiqjk5EfiQFSoC3ghBFVw==" #: KBcX0L2SNnFupJo0+RBPJSQHzgB3VA== msgctxt "P9JbeLjSjBM=" msgid "ZOE7+h7G+CrstUkN65eNau2oRm5HAr7oZo8eiw0CKnYv/D2+gpFy8A4RSBA=" msgstr "tUBxSbzYWm460Mjgn+p/sYT+ELiw0iL3KDgeKfVN9GCdwDfCHWAzEh9mS4cOLIBYSgxg2cYobf3jH7o=" msgctxt "ZeXhTsGFW0EC3ZATVm1FngUefyuGbqhVZMB/eNngSYVM" msgid "RvRQgTx6A5eAqmAgqG0EAVgyvxuuAeGP9zZALcKsI9jEypWkHw==" msgstr "WK+1t/zdp0MQeC+71tsqTi8YFhEIi94vyvEtqzGviEjGE57I146uoDyURhiPsH5nyJnIEcMZcHE6z0D+bqoO/pxOGRSO" #: /GlLn3327KLA0vU= #: x4qD6iMegwQJw54UmqoUi8I6lf2g #: OslaXLhogK7AI9qKfUOMmzis9Xk8/mZga9GWAliOhpiz msgctxt "uvzG6ZjjOePlKsXb4QfAZMOtHBSeFVr18xR8x+mLxLeDZicBfh+P97fijzVl6FYPvK+Aa+XNbVOG811JORyiXQ==" msgid "40UHEyi4S4HyCWQ2FKwD1+jTrfboYmvx6wvOTsOn6JTCVbF0i4GA3EI0gboMCci05R3n+d7iNjLynQX77aclXFwIBGGpYEKwj4dWgz8KwGe+ZeaK/GryXePViOpzSdoX0h0dbe39jzyEHMdgmHkS22Yl+csLe+P55wiJRcg0jRbZ/Wx1f0g=" msgstr "wXhIZOYst1SJiEW+UiTWIYY94KDBaeU38CEJrWQmFWHDi5y8EGn89taKFFzKsF84T9Ein43Nb7AbBvUJbnsDPmctner/zQl1LscjWLJ2R7hU41NxScPbgtYQyXaso2EHdryGL3tMKCu/dqY5fjbUoSxoJB8MBqaZJSk3UpQwth75gdEk9g2iZivRgzqsnFo=" #: Ldl/vbnGLrup4+Veg7D0Jw== #, fuzzy #| msgctxt "MA9kQYYzgrRH+mWbBpq2bt91RtBbPapR20xjUKQT+HKvWR+IBZ+oBiaCQ7SlmhEgcTOic5T99bndh1lqN/iTXc6J" #| msgid "P4Zczd04O+j+Ay5nMS/1QKxCOBF0Tw==" msgctxt "MA9kQYYzgrRH+mWbBpq2bt91RtBbPapR20xjUKQT+HKvWR+IBZ+oBiaCQ7SlmhEgcTOic5T99bndh1lqN/iTXc6J" msgid "P4Zczd04O+j+Ay5nMS/1QKxCOBF0Tw==" msgstr "" #: VSgljGtOKqJwDixRyF3hF3+F msgctxt "2Ro0SomI08IcYRmaR0kDO/BbyHRl0sXOKm0Y5B9Uy5i1TLvquWSf5PIP+hy/dO+p932ZWCAVTZoQuKM4hFC+" msgid "vPbr7yTqZpI3z94dj7g=" msgstr "z7xy5jOdBz5P+yTz3yfy3IV5wP4jzktRoB+lG5oKlblun/TKdl6nSaBQL6PwTJ/0CqpH83L9aTM7gVF7nx4wQFZrC+iifUlSM3FU8JAmLtiHBKtdP3zUorcU4sT7HyE=" #: lpYbZT1qrSeC6DBwqhUnmyCmVqGCZbVOvywqq1C7ZoPh4QhqSfwIn7RI1ez5gQ3x msgid "x37TxY5LT8r63WITLOcb2LsVCM1usBsVq9KSVC+aKO5pMvE0OUjbCIhtvroRUVEtVj/N83jmQb1zHwHOKtpqhaLGYIgUgtHDdkREOzRNUPe1Rdxsrqjv7votqdrc4Cnw9nVp4xkVAf4=" msgstr "EPRFFO+8hPv0C4jwApJ9sB5VbQuEpBjn3+R9KdV7toHwaxDEwUTVzvbS0wne7lXkcPPLX5ShU3FUzfB1g4sCvJ7jT4V7/EJpMP3Q1+Wf9ksg7O6+zzhN9N64LoSj7hrA5Df6SJa9C8/aQdaIDGqbaYZq29hbVmcr" # IOXQBUOmCA== #: 5y+NcCg0FotiGqxLd3SO6ASb/ZDL+MJt2aQ= msgctxt "No9gXV4ofO9jeErUge1YW4g0DZylDALaRNPD8vbPXaqwOb7zp209egpgqTIEolSmhFNoTiikwF/XIRWw/iqUdx6kTRdhyA==" msgid "SfSTpPlOMALMW9E=" msgstr "7CV14zkMtqYMhc6iIhQv3bwl9LHSzlIPxwzAKfJrswdqFpvXJXJDAICGCd/xXgknLNPt4gC57vIT8/SvsYmUo+ienYsy1gxoeBye19H4siCJcPof37AhTCEJ3q7OsCeV8QNwQbQhRarJAPFEMZybciiNlMQF6kH5TsQ=" # N31XD0Q0H/SZXinkNAbLwBlc22zFSUrKp+f/uQ== #: 5Jq4NyVybgPw2E3hiezRm3zto6OT msgctxt "OJqOG/MV4eU/pfOlrtlwz+Pn3WU=" msgid "" "SG6n4NjjKwOsFAbqeGWkD7qFQGX1qW9aUZomE6tC+g==\n" "FoF3I3+g\n" "WfQztF1jfTMyONIhVzWwUw==\n" "wRuG5PkPpuWV2d2SrVRPF8AOuq+3U8FYtMTXVM1oJh8ePpZnqk3URQI=\n" "2AQH94VidimO2bcbXO9Wa3xunLp56XuELNWxpqcSKbw=\n" msgstr "" "RN3/BiMkL5zeGyWUI3uDPQui901e0HNGXUfh7ERTuvJqHy9dyfjMleqYQgATqbJnKwxcLpFugA7ihO6NSs4CVbciZSlZbw==\n" "RgvGm3un/g7xuD5Guyt7eYk8DohqoXA=\n" "7Y31nxc6YPwdYOs1RBo/lR/qJM0sTeXuHcrz+k5SbRm3wCorQJGrd0QiY44AgMBN7w==\n" "UKlYNdj5Ed29rdNq6WyNLpU+GPj0x4V/9aGV6yWT+H0MamZDSt4=\n" "WTpPwv9EJp5HHso8xpegmQam\n" #: Au3AtIktDv3ICuvARejUgr1Q82Jkni+E4qUSA82PS8ex msgctxt "Lictp58GUBaUR+eKlUTWO7XFVKaumn94xQ+LhC7I/N18Zn0GbicK6esk4J8BkxZzbgzVHN00XSBp3F7u" msgid "4rUT4BqD38OTyhoW/nQ0a76oA9cgk4cUPjOLY5WoANMIA8amAq3GLkqjjbeme/cfAP3bWkTm8eAnLd78VpRc5dXhoS1jT7ZUVTt0hZqa8McjrLvgRE++nOfMTugO" msgstr "5x73Kf6A33rYeS66pnOCsf+pQC1JytNOL6rm8BAnCpPIoA586iwHOi6sZcOiM47Hn3D5pgZu0DTaFJda+XZfMpWNwkbg1Ge4WtqEXiNNOZWh55ftB7v8/+WADFHFTbcroej3XiyT9M137J2stH+AcLucbg==" #: pWXM65ZgDuPq1ACf8cCaJWkqvOrIOd8tfdZzvXqexV6GKBpb0AL+V0FNeLw= msgctxt "Bm5Q6LTeGGNMjA==" msgid "f3TvNgbUhv75u8YeohIfwV1HDn5niLzDFMdQRZ2BdFMre4/fCu02TH5A5xQZpSSj+h4CvEGIlVoFe7XZQqlq/Y7aYBgpwDrh0iKyiklA8fcVvI/LkI+VZub/Awq9RUnCFyH2U7P3AuDNizcZyCD0PmM=" msgstr "NYTJHJvslnmLxb47rUJinqeeRT5bUhyqBFPEGtrYhNO1n2LMUia8aV6PrkkZDzZ+fNrzSAc=" #: KQEZEqeCsR27bh8wWYGpc2Ss msgctxt "OZ5oXPTGnZmz1wOUreePBYHEwHEgsCUjflBkQ3lI3HQ4" msgid "A3ia+/5kwzrOMwbBjObp8etVLFKQQVBPyhyXMG/oaBcT0c2jZchvjxTfBsJ4XfUtldLEbppAAgjh4Ayzo2+OTgsWuefqkzFpAfIiYxK8VJ0yHoh9Sg==" msgstr "XvMJHN6zQ3gJMlMh0LfcTicFuHiawNRfAkApeWVgaKT+qn8CtsZv1NM3gF7RiDPbVT2d80gArCnYfdJEdz8KH/XHBBqzCV9st4REcK5pVGq5Lyr6WzXESE8ufuz9UXO7/DxQZhM2Fhl+z9Lq1d8=" #: yLh18cqypytX5j4fQ/XML6rW msgctxt "NHZqmW6LwQwyLWGMu2gyU2ASb1XJ3qMop7dWMUv0fjK9NTLRutYGDxok" msgid "NG7v0fqFdGwvMGUNrsUGzUD/rQ/OS+VOMTaRnVYwpoYR/pQQEH+IY4SsSCy6s0dvcXnETFA7IvImVYjZwBFAZz+PaaYudUfN4q5CvpB6hfhcvIKtj9zCWfG+ZnbT+CRD/3q5Uw1rUgFP/mogJLr3+KapvVM9Ebn0mmBkKpzY9HWzCwpLcafJjpgi" msgid_plural "C+U08uvqdLHGc5kSZyV6d0gxv26EvtI/NAAJdhwPd0t4RnUzoCqW1D4fvJo=" msgstr[0] "jRudvizckh7mSnfBkTrrROa+KZrHUmlw4Wyr31pcxmxSnK+f6dO4YTpk8vc5X71tidB9OlVFjAbPJMq20bjE9MP+hrVVfAR1Le0O6rKeyFEQPTxhEP/JdYW3OzdgPgUrs6j59/PXwifLx59kMiUanVMp8hYlDJfCb0Q=" msgstr[1] "jRudvizckh7mSnfBkTrrROa+KZrHUmlw4Wyr31pcxmxSnK+f6dO4YTpk8vc5X71tidB9OlVFjAbPJMq20bjE9MP+hrVVfAR1Le0O6rKeyFEQPTxhEP/JdYW3OzdgPgUrs6j59/PXwifLx59kMiUanVMp8hYlDJfCb0Q=" #: m9br05zfZs8R4bpHbp1OdPdAVSK1 msgctxt "ykiSq+NSaF2NLgbkiQK62AlcU9zBZU0=" msgid "" "tvyhGGy+JedUZCRGTCUFJaIE58Muh9buTw==\n" "ys8f7V3QwO+hj6dedhhzkvTfYJ8PuHVqG7zTC5ULkmu56oJYA43cg7+YAMwmVxdX/SDjVLpGiPPKWycMPQhhhQ==\n" "xyY+\n" "+FT60nO7uwb/tYGf+fzb1pOgUtCfrDUwyUhU6QcbO1z90MrKkkji\n" "SuupGKBtUTJ9sENZ9Ko/135s8bSzi+rDCTIAembSZntWEejY3b4itWoAxrbb4tfX\n" msgstr "" "bbRbOXKjslMArO+y6Aj8kun9+XNVugnk9DgvuHnDWNhTpRJvMbAIQ5hkehAPKJH6TM6116ZrRvKjGQ==\n" "S2UggiaVSPnhO7dJKZXGLI1HHRxqs0FYaRwaD95Pxpgdf5LGnP743xSv\n" "hx5p\n" "wmtMUsuYKefDjTQcBJX0xWGWDUWZealJE6cbY7ifR/XSAj6guYd/51UJl4zZ1io1gx4RmRc5\n" "F5ANwy9Nbpe8sZ21ssnhUKc6EKyprZd+/iGhVwDKLZgeZNeBtCAKjsUK9Ftr1qhjdksM82U3L7JUoKONhjWQ8DdU0TI=\n" #: Z661gwIJdsl6Q3aw2ugUwQy3JvoU7ZdEgLc3v+rBqpwq/g== #: W6YKARtWQsVNWHbasrm26uDts0U= #: T62DIcVqWS9GEUBdd1yabnFNRKEfHOxQpsepjVSYSrahGM0aoWriMS9/IH7JQbo= msgid "" "1dulHdYQJ+1tk8wG7bpSJYNu/uZWA1dG95gHkMSxGbijLNQN7V0=\n" "nm7U6ZsKDgLPrBmhyvy9JWfWWRRR46q1KOcioGpvHlExEYJVZdFlYodzAa/67vBop0/S1NES0GZ+LXxemV+iR3VoEXYy\n" "GFLVsh5Ezi/M\n" msgstr "" "jV05G+nNwsc=\n" "dny7awBu5NhNkIK7Sgo+ryVYYA==\n" "OHv72U1bXkbj/jjybuXw9gP1AqFugy+p9mIw11tN1Qahzx1jDYg=\n" #: Fb9FGzpGlCcQCMT31vk5voEbAQhKJ5QBzTb6yTaAMWXxr7kIF7CQHTspkS3N msgctxt "4YvCCoC0zQ==" msgid "ZSel+zsKOGbNzwSxe3ALdtAEcgpyxXEIFS4XhILVEdyZofFMnMEOh5EdaT6T6+gMiQOstkPFTLL9S1Y2LVJ1M3BsMefRypau9so8WprZy1tOVBuADFLiz9OBCElaIswEE4hLcOSnUw==" msgstr "shY/j5cEVykSquFKg4xmdR8PtpjyCaa4IEVcnWVT3VLFKsW0/qQlkKZH1TYkxjwuAChe4w+Y3t4ewNbkl+TRt3EY6hnN9eiGpOBoaVyve2E+hiyZJvqRaYVBsGuArDfYF7wtAN7VoqGlCTsWOfT+yVxgnptpWwd9hyEBZJQrI2hnpHKRAvmZbfhnyx1qKNj0Pzdy" #: w2NVbCFGJQ== msgctxt "5QrJB9nRlnpy/pM0vRPpooi5r2sW7wSlHojj/Htm95xjG99tfgQjbX4n9zIH1cho67w9FiyNSrTJ6u1E4FD4FcWb" msgid "QvFynDK3a4dsNN/8F0ZTGJYNFVAbcCJ+XEaWKzD+IVA3uMCZg0tW8M6v2D7AyVA0u5u9uEE0otcueqVCWvXI4w7AOamMonBtXLneSCVUrTB+d7lJB/xe1454W1JFBMKP7TCz0uzLANjG2oc=" msgstr "8FUXsUZqb8145038cPyp7S33MJhglt0uwQx+vD8sOKOXCQp4sEGveUFXmVzXOZc1NLH/cU2aBiejotG8vLMkUCgD7g==" #: rsb8CWA9LBAOJsu6lj3PE5mxuvSx msgctxt "3DdkBDRvWRIDIgkPp8mrHHa5pzUu5veR" msgid "ZzLPq/0yo1c4ddxzwTBaJzwihYe3OwfDK5ZWTthVFV8j36qR/1K/8uWViQ60ci9Uu6hpmyJyj2imf4Pvmw3xPF7pFLoj1hcvd3WkOBqoxYogcS8nsaNhM/rmPSCqNw==" msgstr "yciENVfyhnCmBLoObf2/S78Lstk42EQ6uw==" #: LsbAxFcWRsyXVdc= msgctxt "QP4clyb3mfvp6I4D75ogod6iuB/9MRNNg+k/j4SDjC7qk0v1u9g=" msgid "/sc0kHmHHzt1R6Ht1utBJNSbMoyAwoNsoTntzuCSppOZDYNuc/HhMzKUK8d8ijauL2r5hRvg0wDeBUwg9ZVuyF+3o+3VDS5qPLL8+RQgYXEQvBPdsuTlDOk1AW9iixqn" msgstr "rFXi8QouJmNQptlOWHlASgubc5DCgp6soqMDWUJfhNnTUYYfjXpaHQTRQhmqZALYHI4r0NCO8loCmSzUZU2LRuHRHzLsjL0YdfroZaNrr+Il1HBUj+cyO844GrPxxUfpCQneOXLwQkffBsV8eXogb3cseK94V4vD4/ts6qZ6sZ41k2EMiA6ZpdC6jBtwWmoQ7I5W" # KQip #: mbJEIT1wi6JKpgLjkVmAlqTlfQ== msgctxt "Czt8xXs=" msgid "AlWgJTaOeBdJe2BYwxCUZXFGRlrvSuuSTx/ho9DCKThkopVR9XWOtm0dUeYzpBTmjZ8MAujgHaZx/nIMI0xUAijjQ5NApe/coVGAiD9smnUk0/41IrP8PbFglx7GzJ5juRC3yPslO0rMZ88Wko6TKkGZBotcPK+yQSe95e7eQd4YM+3aeY6bdsjdk97NW+DyVngT" msgid_plural "0/WkCK8cO/nN761oERa0NIyWWCYu6woS3iZ9eoxUa7eEHdjA377VlHCH6lBaACJtNi+8NdlBJZLvuAWDrPy5hN33VfKTDh30Z5MdKICMnwfM4llyGLFgVej7PXQXspWcMfXDxBh2ohQR+QqCzyWt+TfO2VQ8qUGmzA==" msgstr[0] "mRnH19/iU1M2fosgQylHDhugaUVYoICXsO2rQus7CuGhaW+4wYrJMA==" msgstr[1] "mRnH19/iU1M2fosgQylHDhugaUVYoICXsO2rQus7CuGhaW+4wYrJMA==" #: oRxmIAyxjLBU+8jR0ctGiBq6IlBQrHzBWXGNPLIbS7iHdgBrYifs5lFb msgctxt "pif9XzxeoEPw/auXKL2j9fivmE8V8xxtqVQuZnhn19IO43Cm1c+NJNNLrs8S4gjGNXElnXDzS8uTFE08Bj0R2vH4" msgid "" "s/q82T5tKaBu+TE=\n" "aNhqrRmlTkMvmIlI4QxywhRlgaxkQj6OyfqX7GQnSSA=\n" "yw==\n" "ktF6Ow4G+Fk3Lw==\n" msgstr "" "SwzLLuLWs0Ruvl4/7Aoa/y+cdX1ZoxGqrEBuVt0WweVSgkBkTzLCw95TAX8LIYUhY6LlF7B6bmQAJPGAfb7SJtM=\n" "TVvWjYDNkvfSQcmdMET5oPVZgFhDaOVRAmZdAm72DyGBH6/N7AIv4CPOS2LPUr9p5qxTJWbAJvBCk0w/ns+2kImO8Q==\n" "xpfYe11eVTdQXcFVVuS9MVNSoyNUkWGfMKvV5Ff1x5SUfJuagyuvohGEUL6b\n" "L9G4Ht+yaAE2fKFhPg==\n" msgctxt "K/0zU+Fvf9ScWOCcuQlvRyQktEsZFBiHGvPGBIc55M9oudaDx6Bf188sGxk5My4VSBPnzYt2wcjfhDnIz0pgEQiyzZuhlg==" msgid "" "6xxdQJw3\n" "HmC5ULuVfPXRw3+xGA2p\n" "PTJJwmmpzJuIExi32Q==\n" "X/gRBNQFeewU6K0Bqw==\n" msgstr "" "CwhdKq1kPtJiqw==\n" "Y0frt/5tvihFdkGNrGDdsUMp9xbCnH1Ob2N76sAwXMy8j2P0oHUqI8ATGSdmKnsldjTjh4X6Xm/audYL988DdfA=\n" "HFCq2h+TXwPY3AuhANhUUnA14DfoXDcxWqPHig==\n" "xiPjciTVdvgdAqI3cmgwLl36wzrfPZEzKgEMeagvluuOO/QHlGc=\n" # Oyy6TRpMzp3miJjguHaSu4I= #: yE8DZCRCV2j878xZ+7rImaGmmCJ+cF6ouMBrcaxO7dth0qFfqQhIMDKo msgid "V4GSP+QvVTTHlbL5y0tZiFKx2VpbuR3LNceX0cIb/n95jde3X4rS6Z04S5XM+6Q7sHO691SEyqoYvYSDyIV8GmYMq+rCnYLLSiS1oSv1+ml/EcFVP3YuYF5sbtiOyv2ylq3ohIO47le8vOE=" msgid_plural "/KrW41JOl6zz7DdySbCMwjEs17+/ChSAtsspymnz+R0B8WBE7IXXE2hB05LHPyYW93fDPJcvi6XxBE4JfGiC6zxaKQvD6a1seDp+DROnDK/P4iIxkFkalw==" msgstr[0] "gETyo6mI2jSk+svo8tXL/ztkYcH3jYHXTcZDFYfrR42GoC2R4RSfhqn73hYgB2zT0l2VBft7RnQOQ/TQUR8maoRZFrkAdy1fS3yR+Ni4TBC6Ky3GDlgYUKwZE1K9c5EN6dkHgUywOZxoRr/rxXcLu3w=" msgstr[1] "gETyo6mI2jSk+svo8tXL/ztkYcH3jYHXTcZDFYfrR42GoC2R4RSfhqn73hYgB2zT0l2VBft7RnQOQ/TQUR8maoRZFrkAdy1fS3yR+Ni4TBC6Ky3GDlgYUKwZE1K9c5EN6dkHgUywOZxoRr/rxXcLu3w=" #: DCSyOA== msgctxt "76M52HdC3uAcsK8KQCp6S9j61K5mL6sODqsE1NfJosKzYxAMMSCaJ7qFuBEO8Sv4RYPbWJBIO8nFZoFlP91m9uld" msgid "U9Q/QR/G35aUkyO125nEi6ftLLQFsloKFn3nQbN7ORqReGofL0tkp33BLtoW7e60EYIlV/69Q2IyZPz9DCkH5tPCd9IwBCvMltHAMBO1TPHaTzrLrk9lNMVaz4o2Ee3Fvbx4Mh6u5PxnDEsMA7xEfU34U2632aL3HOJTJ6g1DQVzAoekF8y+BcYTGgRjHHLWziNI9g==" msgstr "XLerplnAVvocoiV05b3REXiBqLZx7Dhd2gaIATYGUhtM4rJbIMUuKrud4SuOZZGsrVEXCw/h83r4m/PnCBHLAUw81WEOwdg/G4UD02Icc4ATB49OQkSNDhYX5xl1Tr12oedSK2xop936MzsCaORy4HwZJwQc7h4KoUfo868p" #: msgctxt "XnrZYXL6Qut2d6EwpVTukDWBFA==" msgid "kchg9avo9fTxqE8=" msgstr "XG+DB2RRcPCDrgX/hz0HzirUDA==" #. uoD9IyHe3zpztw== #: U7kA/TJ9a2TLEMqn4jX78zn80csTjwXF msgctxt "g4YiFeMYXKSHcDDBhJ8om4muRikUaTjCCePzBLtEgUpepY8C4v9Dd4YRd7gl0fPICvHdzg5o3x0=" msgid "uMypPPoBiMq2KBHjJvGf7tbr5F46V+y2vB6f4gopzApawY4NfGVU4RcHhlaE/douNsLhOasaHPVtBr1+s+ToUj5H7QQz3VkiObDTQa0qmBNX/BX5XlBGSCJrl8JTTBNRXiw2XBZjZ2T1K3x/9Q==" msgstr "MHGNIVjfM6YfIDSPgBVdf1Pa9ZP6ADyzVJT5ZCI78NB8TsUm5Rzfhkk4U3Rc8+9zxcFybxltEek90mHJbxAFZ7PgK7CHR2mXnRZzfSBeNe5F8nEmFd5BTy69IExFbjW6N80brtPXHpt5fPOEqEX6lJq77K3lEZ0pcpvbIc0E4sY6DZxVxmDZXYxcwiDH7cSG2A8=" #: Bx+QD/fcJiox4HQIPDA= msgctxt "61+L6BoHilPHHn++XebJ7p0vGKWBDpXJloQxAms6TofYdj4wI92mS9O0K7BGHh/Suw==" msgid "aEeAOcnZJdJV0Vk8sd2witACiOhrsD6C5A==" msgstr "1wBl+1lm33pHoyZiQ8ALGmEMMGYYYzdapTQpqVUnPr/h9hlveoLrjz1PiU+e5ma4rG3Bz8YPmth6rNsOOURLnNhiF5OUmJd8enyYKCo3XE3rYfG1+h7cbJzy8cSseDbFHwpRt4dZXx6qmsY+KK8AcmEsbcx2TIG2g/MJbPs4O/CyKGxbAZ77ta3ZV9c8aQ==" # #: DHdjshnVDd7mrIzJnA4cV641 msgctxt "39TEbPSsNJmHHspCkVCRcXlB" msgid "MAGWIup8mjDxgw4CncdomwGoxNGRBBI=" msgstr "Rd6EpfL60/wxseMGs+nsRwFq89YIXWsuyhG7K9HH6J3DgI5vqnXGFfIp9Q==" #: HtYMuEaIX4VSaWYS4tNHIoeaJVGATQyFCghz30UZETgf/o9U msgid "4eOK6TGVvHc4O0UVNbPL3Q9G/F7FvQ/QxjMLSjHO+XqzyFJRVrBjX6L9vRTe5qBYsqAF" msgstr "Vpbd4kwz+Oa6fgJf98khaE4tilx715lFF9FJcpR/VTwzBy6LfdeIOJ2V/pxPRYQAh3vv98R1Gzxjt0eDhzqn8ALzQbh+02y+rXumMps3D9X5k93W6g1zU23AHzpG9zejuThfdYGFqucE9nDx2kYQuyo=" #: ey3JA9u3mWJ22+olJ4lxnZFw/ISEFb96OgqcJElBQvkHNR+csLVM66b0+oSEDiWZ msgctxt "VGFRnitQM+uQyxhxHusQnQMr7fmjjTvwVzMsSCk=" msgid "nQyxVlwlfUHlYAftnmrY1XoKof2QjXoCTa7vokf7hw0AOXjkTGxVv+z2Q80pBPYqf5NtIR2BFvTH9XG/0jOHHcpXhEG91Xexldva07ITbvQ4loEdajbMXg7PonQOseDnuEOj+83cLeA+1iyFKg==" msgstr "sknafTJ6bJhzRe9j6SecYsKyRhx+L+n4P+dMx3BvWympwztwEoGeEtTfWtFJLsRgZiBFkWmtFZIG3i7oKfDlFifPGyo8IPenPrgbguTjbG13apT0nw3sZrYcD0NlWI1pa3g5hk273Aa1pq22" #: 1TxiJ7xw9YjDeq0EaYUCuHX79mufB6LoG2sBC+M= msgctxt "u6sFxk1KK2TI2K/9q1htIkFR2f36OsyBt0qwkZBeMJzVK/Q=" msgid "NtGVFL/Je2ycCVBye0BiTPdJxusxTItJLkK9ECK6MnOIfhLb7zi0wehUv7bPxygMo5DsPg==" msgstr "VYEwPKVG5DHpx0uEW7uLYiKUtD8tc+WlW4M+K1PcKcGF+L+hnFamUvJcdIfqcA11lLCL3LhUIpfpkN9msCAH81y2HxBg9hTbLdHyuRSyCAA6w7hNNZtiXhR/2FJ9s7ttm2YN+onHwiIcDcEm7EztEuIsAYW09ypyHIPSz7WV3XsM9+3NQJ0=" #: Suj4tMK4HSpJL9M6yuGZjMfD msgctxt "fr5J+U014FVo+dCvfJ++mX68oWahuA62g++0hdv2+OTb7Ul+zn/UvA==" msgid "iineIdEFqPkUa4/wOqIAG2i+ijJchxXTO4c0eBZG6TSpoiUIjOjSZQaAi9MCpjo=" msgid_plural "NJUmB4aX3m05A2+BcRnHM+P83TeNwLbuVQRBqCTHhQ==" msgstr[0] "V1uhdCN0qXR4MewQhIXhhixa9BiS2aeaRLNUVXGNlpAHpIeGbOGM7J03KdOfbwgeIXY6eM1qxjZli/q+TnnuVBidSGJrLMN/lapt7Z6fFHLPNHxzR4yUyvnG2v0edjUaoTP6kTu6ACkUvHGs2XDQk5xzAgso6oOOHg==" msgstr[1] "V1uhdCN0qXR4MewQhIXhhixa9BiS2aeaRLNUVXGNlpAHpIeGbOGM7J03KdOfbwgeIXY6eM1qxjZli/q+TnnuVBidSGJrLMN/lapt7Z6fFHLPNHxzR4yUyvnG2v0edjUaoTP6kTu6ACkUvHGs2XDQk5xzAgso6oOOHg==" #: dEMHnxEPVpp+7BrmoFuPOg== msgctxt "jqCkOmfxH2poKVGaCTHvBrDXDNXhfYXSl5T1ZP3vBUBhi+/+WjtQov/bAdy9H2593cE/HZbo325OA1lCug==" msgid "xhkoxKsqjVrU0AnknvLvznyCQFFwlmOt" msgstr "GlNHAQqRRrEMc7+AD+1rdJsUjaa+9Lyehqp/Wzi+JOiluBS7sKcQbz6jW9HjWtBwI7F9kXcAY9FBciBlegNou00q56dWLmo=" #: bTaS21Ota568UdzKPJywQOIW msgid "Ts6KVECaqqMc75YiKqEl0VIg8jLgO/U/jRfFxhd78uHBZGoJqFeMag==" msgstr "Jg4keKS92fnuATTpOJy/Gd/i41+2FTAl7cYuKSd+6uOwXCW3l3H+HyTnQw8eCR0PhSyIcc8i4EGDUAi4+ruG4Bwsi01UJHmFp4k=" #: fPwDNbfk4K1kXHGbyNnC3jd75LKHROf6g71TWhs3j/3ctg== msgctxt "uc5k0kn7l5gn8V25z9M1USGPIjqjFMvQYP7TBg1AFA==" msgid "Xh1qzVRlqe4VHqdWU2FGIRATHSUrKm7mIzGk5WTfyt6Z+ZrzyRZohOAfpfnQTVItrCFI/1BoImIri/BRLjlq1nMpQ65GJ5LFOy7DGor8fblNj9ky" msgstr "R3Ef" #: PeTWD7/ljRy1EKZEww== msgctxt "CLnSDOc=" msgid "B6T1YTrqyxy6R0xHXz3auNiRV0O+7n77Y4jbyyXeYaR452LNfdiFYfdjJ6TstWEhsNK6llH4RkLMBlS4Kid9wd6z5mKkcnqVVMYJeMyTDnxPqSZUogT+o7iUfuGHaHs=" msgstr "VqaI19RovKmpwEWekXKM8XV6HTWIOuet0RV6hs2p2Eso8P5b1vNfShBPgmuvK5j/OfSZIUB2bB6kgWKVdR9J/vAyrp8/xXU2ntCDp64kb61kghrbUaKIluZb4tjnxQ+1hDSEZO3H49BqfG9hxK78vlxJWKECoRpcxQq7RqAhbxhrPC7xs7eX/pl6" msgctxt "SJfgdgzKBE9Uc52aIdMpkr5d" msgid "9eALjVVUj5+3iMqPkg5PWSXxkwrCMP8CCpTWr+R9Mqq8iO7NvIWgwhy/+jUgWDnAFzscQjLqWPHLjMDB8VTqPo252bJbDCVvVgjS8JPvPho9R8k=" msgstr "b6Sxvx/vi+Uh9rgyY1OKy+qGSVPxw+LzfGAoo6eD1Znc6faaTJuqoXoHvesCAAGhpgNNFcnGadHTKiy7qL/voc3qFU9mHuOQrRSep8KI3QraEgy0kRmG4uHD" #: BGXZ/98sRgd9ds3BNJxeL1+1d9hj1UUsGmkq7u7wTH/y1Efe #: /cfp2BgfazNvE3WpPg== #: 4qKBsMYtin9O4XCY9avpKxTyaAB/QTLsj1CcwQ6jDZySgayC2H0avvf2y2z+NA== msgctxt "Jpttn2YYpLpBQru0cfFWyrynlOZmQpPaJjxfaabNfGMzooPeg5R9/c7DlW5H4AdjHJMmuejQTp3SqvEp3M+9sw==" msgid "x5yUhPlLZb2hSPkLx9mdaPUhm5qqMiUQwEIrZW0rqSfOylC6D/nZGEB73j9dI4xa9YaOAu3d1oAqg3wDiA9Bn7SbIEqXqCLMu66USthk0vipFij92U4OYcpO0by1IFCOMpf0QkpIi3O1bIaKmVqwIInO7aiMjpqq2Yh5JAznlR8=" msgid_plural "B1m6BVcVpMOaivMaSMAdNWuT3AuewwJh1b8cAf3vrMtz0Yz2x1ZUK8ZknjIwxIprO2n9eXZhme/z9ERpJL/j3cc=" msgstr[0] "lvZNt3gzHgJgqc96Z7L3gz4bwhyPSfY24msb0nwwcBpmcY17TVCr9W0Ps3R1" msgstr[1] "lvZNt3gzHgJgqc96Z7L3gz4bwhyPSfY24msb0nwwcBpmcY17TVCr9W0Ps3R1" # wt9+baheU4wmv63HhUxWp3wAl43BI4DuZ2gwPn6za6Y= #: d6awDrE7Ck7xyMiG0uL5xalaz/n0 msgctxt "SzRM" msgid "aNDUcF6ECI3sDoS288ae5GhtzVZZ8jkAE7YXRnQquBWptRWq1nuuZm225vZyjnR5Ix7pKISpDarPxLttwqj3APUBRBeeTYvrr+TYKmHV+H4xGAm7Vil1ODsXdijOWNQHiw5Cv3CeYuBOnRjPYrlXGyjLOU1fOFu+zS/2HOJlbr4p3s/WOdXo" msgstr "dxR4ClVz3I6aZJnLg0vuCo7BtjNofJHSx1MMU831Kn1wd5lornHzNTwTHAdw0ot3YYCa8CpluQXBvtCgVUJTYmJoEFcyMjUE3wAkkii+Tlm9LPfHqC+213knTU6lb/wyerLFR2dLVq6RZ1mSi00quskUvBJYwO9s" #: +iCLEA0cpHJ9Pm+bwQs= msgid "KGz7R6JzuajmNEs=" msgstr "sB5Sd6Ykfn57yZW8/Mwl1zEKl4xjktcwnuXqNT27lK6EgcWfSe+2dX3GONNHUrWfNtnxp7h25Kyx1qR8pJjtl9vwJAUVYSrFJ8LvVjJPhMEdmaoK/76macatls80z59fPTa25E2119IOlysGG6mBl6UgZ6ueI5bczW4OpyIrG6OZEByEV9k=" #: elCgqg23pQRghQ== msgctxt "ulU6TbZsVs5YNg+wHa8RdH8BNWHirLscCaShrnSmXdk+8KRYdeDw75Lo8hp2ZpXAxCU=" msgid "AD91/kHKj4rubMnaNuR9WSCgriXkY/uVbgoToH1MAT7vKRs=" msgstr "DnfAAFJi2ibG70EYlCrtyESb2JgyLPyjSmRYS7qKzL0xdndQZl0UQx9FUjXm5g==" # bFmA+D5oymIwvGH7496XFwLFMEoi7oY0wXGkCCp5BifO #: OBWnYqKKJ1DT5K8Ysw== msgctxt "UTWKWyiyUCbd3Dl0dHgsEUKudg==" msgid "GD7JHFHwGm547jZf5LXbj7tqaI+H+gNFx/e/CK4MZsXggE/9zx9YNKVk5XpqrazTZamq2DmTJGEPIdZ3YoI0IJqlBYEYZjqJWLT4T05C/W5Fh/wANx8kwuxqZDMBNf8FB//xlpFVJ9q3" msgstr "k76K9asEGpyYbchqrPKx27GMemsjfe0RRV/Bj+rR35qnw8+hEfn7AVHV57X5BRk+O14rkifhPw==" # zWY= #: sSKPm2ExF98FtwzIf5C2BAHvWTCpB63/QL77fYOYIDlBUjA= msgctxt "J6wuoX/cAa6XoFcPR0p99H/TNUTJMO27xuLEVA==" msgid "AbmeaNDNOCI/3+kmNNqA9k9vMyVqNMvR" msgid_plural "vTzSwkMiLX2x+/MqDHYrGgFGk6x+hHRu9EQ9olJwJBDWhGBYYJQ8rxB5HMyu4jatAjyddkVFGVNN6XYs4W72ukkX90HlgvaEI7rCk4TCiuoTZEiEOLpqn6dAtdkRki7vTYrxTCNcQs1/RCoLh4KlLE6Vd9zxCFFji7+F8CoISVM=" msgstr[0] "vFnpzBOiukJEoGKwq/3jVQdE5LKlZ/cOkTa5BpFx5qWG+hOShIj2jBy0sO6C4qO7ESLjA+Q9bS1lCgRpNbchbZzcR+c3MjupMwnQODFil6zQyNjnLSM6" msgstr[1] "vFnpzBOiukJEoGKwq/3jVQdE5LKlZ/cOkTa5BpFx5qWG+hOShIj2jBy0sO6C4qO7ESLjA+Q9bS1lCgRpNbchbZzcR+c3MjupMwnQODFil6zQyNjnLSM6" #: vvA= msgctxt "lOXe2Km40RiuLdNOdGhPNpeHRD17zDJ8wnht46XRxgKxjI9IU/nOd8Xybn0WIjE=" msgid "EqcaW9FZCeMXzv51vFG1jl1y0dZmOBTuYoPOQx9RkUIBC5FDlBGLUQLIKnMpZz2SLsSB7GdjKXiyNQVBT94OpQo1xkDG+lLiEedUv/fcriffXzeX2a6xqRg/1yfw06gPGtncOzHhBN6M0OXh2oAIgda6ITWR3HVdr5asx5s0" msgstr "zaP3Xs8pzvCiGUFND2NGRwOpnh0OWLVJ+igaWzZkbFld56vjJ6IfFAZafAtF16IkzUklucpAZRFd5CFGyBsV/WSYMw==" #: UsPNsOZhAC9uNpYe21TwMK8xI2QNXOwfJlYIOp+fsP4Selc5o10GeMii8AE= msgctxt "X/z5O/6hYkzh+UFC/kWPg0yIC0RLKRNXhPiJbiddWHoxmjdzX+XoNbpLp8ppSWJmjIk=" msgid "JTJdCcFulylAbUqk+DSZN+o/C3kL" msgstr "2SJOpT3gT2AZS8MnpT0xoAu2SyCD43dJKzKVSrvGBZS1dYgnZ5Y=" #: TzydhGyfoGpK0bCrkuMdSIyLZxlVWtE= msgctxt "zkz27r/Ess54uPcr3YnPlvScykDgnZuKzAWvvIeqWFzpbBq7kA==" msgid "2TpWKX16tzmuZlL7oKshCZqXOg1Bj3iClCASTJ0ZuBVVRcF675UTAjPxcwZoqb4/6xg8S4tTqXiT9Po8QbIncXg4NMY9A5IHoedoMTPFjCTAAWViHiVQVAKDOHcJ+ZutRvM1zR/6etcyZYT0793rJ2cuBnvZQ/iX5fb/i3RIYIJl2TxgU0RDZBli" msgstr "wNKbxogyyELIqetEjsaLY4RB1fz1RacE7xrbwdoIWK4msn1ELLoW+v+uQO7IZo9vrvLl2RFXsngY9KiZKU2dAQCTCZB6Q16XktvL+Twa" #: wySy8jvJ4cM= msgctxt "ghn29AgPI6PQl7IiEEhJi8S78SDKWtsjuxQxfVoRb7PO0yKqtDBxvm4+4W+2tsVNxc49nFBr4bVwpCpJiBnMhyHwKHI=" msgid "ayZ0/atV6xmyHR7RLZS4PYrv18nVSCQaAb+fgMkiE0JuprEUX/1TAvH4KA9+W5Fubm4jKCdfwOTf4a+YwQwGlpuA5sz+GD4WC+0kcdV5VCOU71sQdPCJBLs8LaBiyPgCg+ovgTBIHRYMgg7Rr86+nWvMQX2SQoE3Uh20unUjN7Mz4URVmhye4xp736f3" msgid_plural "h2rc8lb5nj+7H5YC78EssEn/ezLi3ZR5atugeADaU+0PBGJWj4QawEHpBKeBWZwUdA4Deeoab6Wq+bYgFz48WdLrOQcqwfGGdEI=" msgstr[0] "0d/8yY8NV3WjOje3HKf9cIv8b9v1SYZCx4ix9PrhK5whNdB1XjmNy4WBS7LJ5Lsw8PlDITPa3QzmP/rjwPeUTABGUf9hsSKukJ2gY1e8SpYiokVG1Ei1xgImBNg8CoBon/W+szEn1g==" msgstr[1] "0d/8yY8NV3WjOje3HKf9cIv8b9v1SYZCx4ix9PrhK5whNdB1XjmNy4WBS7LJ5Lsw8PlDITPa3QzmP/rjwPeUTABGUf9hsSKukJ2gY1e8SpYiokVG1Ei1xgImBNg8CoBon/W+szEn1g==" msgctxt "kPhHBGtmlz811ngGFAsN3r5fuQX8YnNLhvridGIpnzG36QFZdymid4Lyhw==" msgid "pub3d+myiABHYpm0WCCR7y11Uh/qPSDLuMyxFRK2R3sYdIYNGvV46QeOyEW5bIA6d6fP6SKgETx+f/tRwh0aove2KuglOAIdwY/ErMtNeBZ1dAzCmJrjAmVADTk0PQ/qGCsq/drue06j" msgstr "COvjbT0g/9Uje3jxHTxK1uwXF2Y2Wwg+Zxlij1k5a99ygrDDtrFL6lO1cw6SDw1nc60=" #: UmX+S6J30HJvvnIYUGbQmzgHeT3O msgctxt "nUUuu/y2Z131i5hERFU4vn9ge8sXv1fai8UeTRN8iSxFQw8WAYk=" msgid "oxaarTfHCs0EL3a5El6ssl98CNPrPgFXLOQFkzp69WFvM+wt" msgid_plural "80eWmAHWv9XYobydQ870b+xwv/FeitIE7tCdWMVFooaYoXF5t58JOWMe+xN2RRGrJVyg6lVMHERR2wfv0fUdz+T5J454jbeEwjmNC3QNbNkI3uTmlGw/E//tbQGsHJ8pZ3ArDhR2ot1SGaXvtrOfVM6M+jUVwZeaPEiHZmUCAg==" msgstr[0] "4NX5GFfq" msgstr[1] "4NX5GFfq" #: 2de0yyWB8nuQAlZUdf0GBvUvhDarOm9W msgctxt "iGAmDGjqMIkLiEO1ocZ3YhthCC2tX7o3XX5DJf8Fu3edLJ9YVg4larXZ/Q9Jbl+xngq/4iKrg+o=" msgid "RtgB+sA/otgr0fNzL1B15aJSXBQKLZvSHvtKTLFpHpkEzw==" msgstr "fbC9CwY6upFiblB1s8J6fuX8fDI+TvOCpwsq+xchUsDKeTUIFEN/jgdPi0qj2Fxz7RRm9VsQ4Km5PNhMNwdX" #: +mwJaOb0M1fK5jm/so/xKdwM4ro71aGDuIDjUYctbP6S7KIPZQ== msgctxt "BgH2" msgid "bnmJMulVznyhuw==" msgstr "OhJe1GLRf0JbtkRuUy6X65H4MlaPAoNdCFzl++M=" #: eAM7RwOAyJ+IZImTbcDeX2yJTRvTQ6sRE7eFbxae1J82cYZWtlU= msgctxt "jOGywJxds71/nvg13FHH4FevRw4=" msgid "Reh6w6Aj8/l6xqLjCGm6YQe4C74=" msgstr "ybCzfo9Tn27bzZo3FHDjZjlbKavecWZCTuA=" #: S3hf3Yn1VTPCn0IAiVmCzu/Co61dSOydnZ83AGDxs211xSGeq76L0HB+a3dM01yWWA== msgctxt "24fDiyg0QDgkpCvlz7tPs0VSnK9wlT1+IIQzUFASv6TWynx7Jd+Q4tzK2T1BHEwEwNOLLl6dffvnqg==" msgid "oqyYLnwDKxnd5/SOrAsC/F54QSz1KtC8uABtN9IxJIAxiJIG8ShWjvW5j70s7OPriQnPSHxuZByDPbAgwL4E9wZBW8O4v+mPr8UyEb3t9Me26ppOXNC1/YJnijSVRRgpIUefw36pG5nKB5gXTzg4iXBHvEJFPUL5CMyPP4UsEZv7s39Xc3tg" msgstr "LY+eBnvD96xSMHLiBmsHv8dD0k/32YVYFZZxS4vS75qO1R8z+mcaRXk8qVPOEMH7v0zlAjAFfPsvHnG7MSoCdVHTn++mQ+RI4pBJnlJF+dJz9c/WlFXKH1ci2TGqbu/RaA8UzsAsNCWemecfPc2wfzZS48wseqA1QRBg0CTrGw==" #: S60iRtYQQ1gWCvf+q690XZo= msgctxt "4Q5Ew37jqTTJfsjnIwwJYPXYymn5YjhLwpkjBj5vIGl/v6pyDKkQBfE=" msgid "P81LeLgzMJZWB4SMEE/wuMFLJ0obSJEX/uwmbsRKXQSPlzIpzMzjzJ4aejE=" msgstr "jEZGH1T7YeUrAfVrudxTeyl3HobmwMNmf8AeA1jddfz9EaZnQ95mX1fAI32AZQe/KEJ87uQ0LAsen/F0UrItJMciiFVL5zTc5X6GTV9/fgGzswNCbXi8Nd4VCx6uHS2nfo+FunnKzogX6uchyowOGT3YvjJm56nIRM0y+alYsGH/CpP//x6KL8tewbD/9m5k69XqHBAO" #: qw== msgctxt "+rNeA3ieJ4FfbW1hesRNDw3LqbnlGLeIP+f5wHxNMDoBh1E3yh/DBf2xwa+8yA==" msgid "sMUrmfsRrZ2r+H34ew==" msgstr "dXdOqZHUN0mYwzopQmcwLU1RaReNPiQFBXRetuQvRSYFFxr+C6ipb8O3Zc91H1nKVNiNamefcSKGq1+DmgfUp7EInMM=" # 2NKDaAsOBmUtgSIJ86U64KaT7ZYj6mQwKRHZ4C3HkNP/Ag== #: aQSGag1dajExVHd25rbTW6ofLb6HHUX3 msgctxt "h4AxtMeEEqgOzioHjg==" msgid "98kMYDup44+WiPWrijPtSNbf9cpt92aM7zshjq6+gL26KZq5bOnIukPkkYxtiGZXGQlj233dDWNQSxYlTroJ8rmqwP1rZE/wGAdXQ+HmWTNjOBV1/1AsF9YCzF9XXpi2yCgdkI0xmD1Mow==" msgid_plural "rbW/dGa1ykwwGw==" msgstr[0] "" msgstr[1] "" #: au24qoVztLHDMeTn1bKcgh1TYbRwySKFjCuFmrZMRA== msgctxt "GpIlfpt2CJ8Du7WJ1s7LFlIbpV+WgT2DCf8OHNIBBzwa4pQxdNTQOV4=" msgid "7hmZTyqFtir8b5OKFaoFdf69K3343AieBcV9g8l+U7/343djSw==" msgstr "r653wI877q28Y6CwrRzEVyUYEEPiiXGY1vErcfiolYN+sMd+G11E8T2qyZOa7yc6iTwQ9Kr78H4b0iF3vTTuilc9DmRAqfrO2d7IPd5Sjr4c6Y7t9odjXaRgQ7rEw9NpN+5qZl6WcyG5GcO6r1UJmWJHF1rTKdQpWnE8IJal" msgctxt "OheyNPxpyfeOGmmqNqbyaUvGD146Cw==" msgid "" "3wdOTah/AkLOn4CScMvJl3k4sbbkHrAKwseM8qqHd18KrmhvZn7HYqCI\n" "h/YQJyhKidZ4hzbX7p48NHZnuC+syK+wb8lCQnc=\n" msgstr "" "kIxPZILmvAWE4cnSgTNJ+JJh\n" "+C7P0enWYGjNW8ftvXZa+7TiiG/tZcEqgnEw\n" #: 3+rmK4sHIQk4W8aERf1x3iiCfY8= msgctxt "KO0=" msgid "PQiRdrjPQ2rc1rY+xYFV4w/l7IZgSmwO0LEkyNXEIpinxV6wDmZexipwRykCQD0GO5OHcC1n5x0IR+9D9Z7S" msgstr "woRj9Ec1ll8QXTmZeHEpNTmqtJFDje0JmsY6e86jXZFQrZ+xStEgrvI0GTnUfC2CWuRIIgwPCWMv7jQvbAPoJ0esB/Jg27RSV0lSPxEQu2wAn9X25RjxrkHEvSy1tJlBpdf9ea/aLTNSZfKXJibs9WKKU26QIhD6kHNHpmm7xx3GaqctkXqjebnm/rXbqGAk" # EzhVPinvKt6hNdxKS2aRb9orF1ymrA== #: uIjHm2amINkOjgFgG2l3qcZSo1mDSj48 msgctxt "67e3TuBB/JUvuwbDByFb+4x6ee4HsT7ojgc005RCUwgPUExoiOcIf7rAIH3dHUGUuV9gKkytg01e/f65Wt+TWPF/tnK+" msgid "EY3j2wytvFqZ3Pg3UlIGUDY63d8ZUSJh7Sw4xsw7Gbi+Dl3sb42ldt7tV2Y0KQI=" msgstr "bg4XwOcGPRl3C4NVrmdEeUv8ZnHfphj/kLaDy6JOMPm1/mENpnm4wlX0Bri5M6d9gT90xwxFRqh1DXhHw47wIxMtIsw5Cg5mCFYT6nsQbAx5gZ/dSXmWIoGnJoV6ubio60JucRE2B290p+EytBPLMFdqOcHWvZdPol+8AahkTUNVlZN/VuIeVS7IEWRxU/BluAjIJgY=" #. Ssql9gZ2SG2qkBy0dO+uwB1NlyN/ #: aWtoC8MhkPCUgUjKLPPmp5GvwzeKEHT02JS8r2sw0Plr3T7d6630FdGw msgctxt "37AtQnqLdrTyGwW+iegJcrfQjYhZKK3JHlHOuJw6faLN0RlFeQ==" msgid "VAZjJEGh9axH23uqWoyTMoX2TmJ+eEfZjr3apHd5+Dig3hXZGWXHNJ7pVcQ7XmtQMJL0z1K1EEaMqDMr4j0uOPgpWXRB2aMxQMURcyDj3D3R1vuB28YRozkcTcEo4n40UAiQJ734sj4Hxj1eK1BTO/Dh6Dx0TJiOYWvgKRh2VBnQGBdp/7iVCs14beasX4CRQ77nMQ==" msgstr "1YJCZgBALo7oJhvwrCo9jIyY5gcR2P7gRt6toJvSrSjdcvDlSv9B5ahAZZeWwbPxP2M=" #: S50yV05jLf4J7u+lICw5nTBSv6ajj3ZEcduBNYucCG6xuQ== msgctxt "soI4++eENbfppfiEbWi/aiBjXbhJgmc=" msgid "GYBb/hXbUJTgqiRoI4tjKUy8QUUzl4DqGrY69AF0OEfYOBqXQCH9BA1QTkHfSYCKp2UD2ea4C45vG6OHjWSWkRuu4/1pt1ddV0CVyLN50cb9JT/agnD5fTWrQK0OqRXv6GRmMTanfzsXqRLMe+Roj9WNgVJp+CPYJGk4" msgstr "" #: uRFQfO4= msgctxt "uHTf" msgid "D6HC3Fu5IulHBjBVROAi/6Q103bPkZFNiTZg4D5QJstiVLHTjBM2wVcXEAMBUnAzX7sykrgWocvYCxzudINQW8UP67mStHbTyJZeyIYKAOfG" msgstr "uP2oVAjqDz+4zF7jSeXwsQK9nTwWEethTdFd3MGwdA7eFPYqZG8KwTI3wqhCM96T" #: vID5mvMzfs3qmz1VH47O5HWwBQKO4Z1xCN0X/Ew= msgctxt "9EgEqR/xCvuBjPXrMsbEAd4=" msgid "HqAQBukc3DLwRznN/2qCuNwWXc11CoKv01dW" msgstr "4un4d1x5zqK9pwdLjBZPYiAz57FS090lQIKXAt64Idl8VWvgsmWX7BhLGTQNw08HcveXtjVFE4EAgZCv4+YZCaSnqPhEPYCJGGPumu6XUnMAWSBTk6UDBVSChWSOQMsLSaBNjrxBInaUVD1i0bLeZAanhF0TfmRT0YTmqU9GftlFClUGzIxo+Xb9ZQ330JAqh3P5SLM=" #: Yw9lVeME0VSMYhCWY/WbG2buqHc= msgctxt "N4R33a6KqgKlFg==" msgid "" "4QbZ7JpzOU+XKuU66V4Ddn5maI7jeprImYvaG+t5Mm8cu0w=\n" "l5EENUOC0M4k/0WQDlG9g7WPgAZa8/JL56WLcu1LKEW59IZo\n" "3Vrt46Pcw0CevmOO+ZbnZjnH58fXbR0l+b4EVW7enWT+zFUoO74=\n" msgstr "" "MEi0TNXocnks6ZU/OC2lzBNaneNBl5c=\n" "FirGWXMIxUrr9vP2W14=\n" "TdOGHqVjp1KmskJKQ0lKNA6RH8ww+b9t7fiTFtuYUaNpB4bJE5u9Shc=\n" #: 31bXzqIv1tfQi6JVtF6yvK8NffhONYf/zHrcvNeStU31lVhZDL0= msgctxt "h4eioRJu0ZdtilQqETITgL2CRW1SpnCcHxi9DVP9bHoc1U1TZC5EpfYKYiBdXZMVVnQb/w==" msgid "JradhY8=" msgstr "/C8giZ16ETUlbmTFKij72cQfRZ35OBkw2Hoctn59d3EufHOIhJynV7wRkOyB1jzzS0gRbREhkYTJs2dxHjypBKNMPMT/hQirTpie32Z4sQahEMo07Y18yYU/0W/De1bbEg3hXhBVjZ+4LG/SSXFITb0lrcPI3F1fuhOOfspgsQ==" msgctxt "ht6YJ1k1GGH88uIA7Fq0Ag==" msgid "" "OJUWqkLuzybiphI7kqMDt3ptmB+Ki9jG/HXnU2rn\n" "8BG7I96G\n" "AWs0Gp3P2m5iNhtfOx3H8C7vaIZXvLVMwfMViAyDY9iE6NOjAebHtyGmfkO+bavcY/0vv4F5ARLpSIKucC0LLN+CCBA=\n" "31L9opqpS2Oom3LOI2IMMeUJ6boPVgoRufc=\n" msgstr "" "OSVjt4FVQqvgN6ZVpXhopj9rQIWSvbyhmnSG1c8drG2r8DM=\n" "7jtyko0+fbiSJWJEUZIDt4nyvTY0jXVZswbbS4twGZOBqjP0FAABV4pgVr7xaWlTVcNXXJ/XAJKEGT/TrkMv\n" "NFZ2\n" "syyZ+ZGstfODxKN0HbbdN6GinoKD5WqI06EVokSbTbTRY0w+OsxbNe/8bPvnDdEZ9ILqR712NCBSLQ==\n" #: Aqdv5PW1aoQ3AUMf+N/pU9IAYcidl6D99LqhDcfy27r4wQ== msgctxt "Zd37c0923HbbpROk49UYuFomT9nyo67zrmvxENgK9yHB4zyYgUI27wZ6I9dlMTOioPPVPSnsxSwkpk8=" msgid "aO0QpPwjrEMxECC5vqAkjAXuij/v8hMGzKZWcd+DFC8/hETwycWU4NYRiSF3qL0g8s2rUKSowxKaegQ8oY6PtFrun5LaFFFY5g+jC8hd7eyjPMz1LZwd25wlZPiQLKOJ5FRUZAxY44VIvQ==" msgstr "5MlaoPKoF8TK3Bm7Yui0FUmad95nxthBmIidTMH6tDmUrwq4/K82/vWSvXTA4dD+czQqAJH6Hob9vjsGM0vtjQHPlJdSFlMc4iXSZLGIP2f3RwYMlRHdMue/31rOa8Y=" #: NETJUSf3GRJCy2rJYylcm6jTOu+6yrNglitVGKG9K4kssheB msgctxt "YqYBQo1Es7wUjYRDtU5Io1lUfRK1KFby8wVI4aY5biUv6QWtmyUNX/E0/lj/rpnFi9hHPajv4pBtcYcRpr6gQHogQ3mr" msgid "NaRA2a9FTX8=" msgstr "RLJsUH3skI1VYgD7K3YbfoxPuuvgbypUwq1kItNkYlSy31zqshQYl4mLpBKqafFD7vZZEvIVAHnm6pY04nU008+YZf4xyeJFsUvB63dobs8OcqtlGcCoc2wi5xEv/fWU9UkzkZkScO1YZGuWtVHxJoH3saef5t5wDzuw/FEt" #: ZE2BDSdT4W0nrTIcZJ93tnQqE9fi91QA5pkRkyyVREyye1LW #: q86W9Qev #: B76AD5N2YAdXw/g/iF4p+C8E0HibFzWIphM= #: av6hsTrmZ+HSOef+thJjJjB+OsG2UENgK6ajmwKAfIRjJS7HUlCKub3p/GL1u2s= msgctxt "rLeF3IM2kUGtmPn7nfjrlWGUcJXB+Kaa8sH3k6nXXajcdHKGRG8OiyN9U1Y=" msgid "IYXmn+HNBd0gEgbRlfM9t7KTdNbLABGn3TRAn1Av74fjCNdiTIPWjfa3SoycTs5GTlEXnSTk1QP520OrJmP5AbSpJnHESAsQ0RxuVUZtAwhfLp3AXMT+CKVRihD+Y1W+HeYYKW3gLiBEA7xcjxEUGs6dCN5c/cZzPbQV1ZkswtdoRTLW" msgid_plural "Nn8Lf7YpFqEwFnZoWyxkmEue+2XAVaDEq8ay2bvIPIyccmOvs61qq4t66xJuVzmYiwHGvd3JN8BXuFkPabS7Ysg7osoEJ+cl+fA/GJoLUY+GLKyn33lrmgQahJXEmw1wlTVEr4OEURzw7Oaim7aDyU0icgRauAV7czuZqOIe4jAiwR5Caw==" msgstr[0] "GDh97KWu9dRiRzWhzr8Wbtr/F02791w5fasvtJ4heSYMOnACO0cKRHJwIHH9O6fTtLTwaRL44ZdoVgTe80IxGuYUKX0D3a4cVdmpkODuct/A1uv5nkmqlsX7oKhHBPT397WVcdJ/6ZaExPGzdLZ3U7PUPWDOYeAe8lHSs2NxUpQdrmoAYOhS4Eorkg==" msgstr[1] "GDh97KWu9dRiRzWhzr8Wbtr/F02791w5fasvtJ4heSYMOnACO0cKRHJwIHH9O6fTtLTwaRL44ZdoVgTe80IxGuYUKX0D3a4cVdmpkODuct/A1uv5nkmqlsX7oKhHBPT397WVcdJ/6ZaExPGzdLZ3U7PUPWDOYeAe8lHSs2NxUpQdrmoAYOhS4Eorkg==" #: b9gWXlajpm8HN4FH64C5icURgxXWg/87/tMiew== #~ msgctxt "VwqzMVcI0kDhQ9UJ1SIU0FYF7w9VA8A9n/xSBwURWBI6NlTFqDQAxJ6hWdH4tLxY2vg+K7a9OKd7NAO1q6hSwA==" #~ msgid "APWISazy24dcR94hQumZcceLmsRqzXckjUmwKsZSBRyqkhVOIr1F8nsvB8XHHltx3exLWARsrg9fRvCLvvqjiBX+DnBHeufL+2IbA1CKSQ==" #~ msgstr "IdP5UZnz1dcLPgxucqQCiaYBaNFOtQaKyevVCxl6v69O1qWqK0HVv4uzi2HO1IKleRk8BQwRvw35yk9IRjrjIuBAolWMFO95jQZTATCmHw0bmHlFtEIStBB/hoFS9mqcg0TNCW0AUHCqPJ24cZPuTUdfi9p5BQWPyLqS6fX6tC6LAcYfbxKQ5LnJwUoYTJ00gQUvbEE=" #: 63pv6WqKhyzIEaHC0BuybcaQhSfS+ARMnNzbtVWAFPhAvPZvVBiP #~ msgid "oNN6TWyz3v9MpzcOLtFdp2VewNZVnbxPEOnBUbEh563QsBfDzPcAp4uZNhfVW4ycrFXpEDb2+5JtFf/boGnWLhpUJYtMYcQF6Ny2B5qMkBUFKEBsrvv6rM7EnoHMopomTl7N/CJ38e5wgcEF4Jnn8ls=" #~ msgstr "BbXHATAcEkuwYRnCv38FFtscJhuSIW4BWyzozgHYXfTxzySMfHXREX5AvFOWZvmDZow02/Y+AGxLpX8YIDBA" #~ msgctxt "3fv+" #~ msgid "5zNkMe6/DrRZHCx01iwXXNm0CDfXxKv9EOpI+AKaVVNvcKvKYPMeugjtuGifg1vE4H2UBiaLk9Y9B1WzNRTD7b6/i5fDF9hdwS0M+okOtmy0B+DXUyuor7FSn/tX49DVA5rBQsYRCTzrt/Xz9N+xm6HfRl9LRbKs8Q==" #~ msgstr "zlUk69xBUCajL/0xPiLAiLvSPRX2YFwCSm+Uj+Aj1BvKItu+v9M=" #: olyO+bm91j5sX+6Xgz8z6jhzMZ7QPw== #~ msgctxt "ApEzTlyo" #~ msgid "" #~ "sBZfw6O67GawrmwgAJuRiLBj\n" #~ "whIhahz5MJs=\n" #~ "TQsJetZteEsmTd35T1Ct\n" #~ "rOcttGrFwj3eJPIWr2sChO6Y2Us=\n" #~ "4TSFg7JDEiPW3n/Z/N582C43TxVn2+3xfu9EUYPTVsVMkJyCO8aaTzst7L3W26YPmulb65I1WbeR2y/fxg==\n" #~ msgstr "" #~ "FpQtiQrz+w==\n" #~ "9RVGQqs43UNyy6RSrxyKvvPiV7fKmn9/rSzaMojQGVYzOFY=\n" #~ "jzXg871m\n" #~ "D/sHv6i4yiuGn8Vavl+zGHATQOVjEgGA5kGOj4BLi0LOq3vOWSvpF6F7Pn9W+i4zVcAWwGI7DwMAwI6ZZ6Yt5yU=\n" #~ "Y3HjvucfzuT306p/RmS0zL/pSxdNQAl3CtRlMCv8gYGWwIkk\n" #: g3Ke7wOmd8az9aFdF21pqR+9BsUAnQ== #~ msgctxt "C0ee/5hMFVfKH10y3/q1tW+JcJ2fUOwwSIWVDvSK+gWTlMpBjQiNLSlcRA527tdESHbpVdDY" #~ msgid "" #~ "8Jwdo59XuHi6fF0sepxwZBdldYisC6xS/OBi6jck\n" #~ "1HnzUc05I+6bgNt/DJC3BuXOKlaPKw6bHrwHtiLhYnf8ZD5NLRUayw==\n" #~ "YxP9bmqe54xzHsV+kvOC8nGxLq5l1/iNSFqYZm8lXzoPyTyW+gnNDuprBxWA+JTHtMQ=\n" #~ "dwtmn87DiWuyaxmAg/gG/GTmQMSnttR50+FGMvUJyraXHjJtPoBboXCcnEPwyQ==\n" #~ "Ih0YhukxpcA=\n" #~ msgstr "" #~ "KNV21UxUFdML\n" #~ "lEWV1dXMM2DCDsf/VGJCSYBMCknqOfZy8zxU1464j3wSbPF8+ASTnmodBOOgsJ8QgrlA\n" #~ "b3vT0oXJ0klocBX5GEf/1FxcHsSpz1lr1IATO1HpYknErp+UH3y0MJEJiFJwmNvYWF8=\n" #~ "HEdr/aQgXtk2Y1iopCxg6zdjBnQ=\n" #~ "dsfOG83jtxBvNFf9GybSCKLrlGvEA2jvTYA=\n" simple_po_parser-1.1.6/.rspec0000644000004100000410000000005314217570357016240 0ustar www-datawww-data--color --format doc --require spec_helper simple_po_parser-1.1.6/README.md0000644000004100000410000000422114217570357016403 0ustar www-datawww-data# Simple Po Parser [![Build Status](https://travis-ci.org/experteer/simple_po_parser.svg?branch=master)](https://travis-ci.org/experteer/simple_po_parser) [![Coverage Status](https://img.shields.io/coveralls/experteer/simple_po_parser.svg)](https://coveralls.io/github/experteer/simple_po_parser) [![Gem Version](https://badge.fury.io/rb/simple_po_parser.svg)](https://badge.fury.io/rb/simple_po_parser) This is a simple PO file to ruby hash parser, which complies with [GNU PO file specification](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html). Tested with the msgcat (GNU gettext-tools) 0.18.3 tool. The parser is probably as optimized for speed as possible with pure ruby, while parsing all different PO types into seperate keys. It was written as a "core-replacement" for an equivalent parslet PEG parser for [arashm/PoParser](https://github.com/arashm/PoParser) and benchmarked about 500 times faster. ## Usage The parser can be used in two ways: ```ruby SimplePoParser.parse(file_path) # parses a PO file and returns array of hashes SimplePoParser.parse_message(message) # parses a single PO message and returns a hash ``` ### Hash format A PO message is parsed into a hash with meaningful keys for each type of line. The values are strings if only one line of such content was parsed, otherwise it's an array of strings. Each string is representing one line of content in the PO file. ```ruby { :translator_comment => "" || ["", ""...], :extracted_comment => "" || ["", ""...], :reference => "" || ["", ""...], :flag => "" || ["", ""...], :previous_msgctxt => "" || ["", ""...],# msgctxt of the message used for the fuzzy translation :previous_msgid => "" || ["", ""...], # msgid of the messaged used for the fuzzy translation :previous_msgid_plural => "" || ["", ""...], :msgctxt => "" || ["", ""...], :msgid => "" || ["", ""...], :msgid_plural => "" || ["", ""...], :msgstr => "" || ["", ""...], # for singular messages "msgstr[N]" => "" || ["", ""...] # for plural messages, there N is the plural number starting from 0 } ``` ## License License: [MIT](LICENSE.txt) - Copyright (c) 2017 Dennis-Florian Herr @Experteer GmbH simple_po_parser-1.1.6/spec/0000755000004100000410000000000014217570357016057 5ustar www-datawww-datasimple_po_parser-1.1.6/spec/utils/0000755000004100000410000000000014217570357017217 5ustar www-datawww-datasimple_po_parser-1.1.6/spec/utils/random_pofile_generator.rb0000644000004100000410000001426514217570357024440 0ustar www-datawww-datamodule SimplePoParser module RandomPoFileGenerator require 'securerandom' def self.generate_file(file_path, length = 1000, obsoletes = 10) header = "# PO benchmark file header # #, fuzzy msgid \"\" msgstr \"\" \"Project-Id-Version: somehwat master\\n\" \"Report-Msgid-Bugs-To: \\n\" \"Last-Translator: last t \\n\" \"Language-Team: team\\n\" \"Content-Type: text/plain; charset=UTF-8\\n\" \"MIME-Version: 1.0\\n\" \"Content-Transfer-Encoding: 8bit\\n\" \"Plural-Forms: nplurals=1; plural=0;\\n\"" @random = Random.new File.open(file_path, 'w+') do |file| file.write header file.write "\n\n" for i in 0..length-obsoletes do file.write generate_random_message end for i in 0...obsoletes do file.write generate_random_message(true) end end end private def self.generate_random_message(obsolete = false) random_message = "" untranslated_chance = 0.05 fuzzy_chance = 0.1 plural_chance = 0.1 multiline_chance = 0.1 translator_comment_chance = 0.2 extracted_comment_chance = 0.05 msgctxt_chance = 0.9 reference_chance = 0.9 multiple_reference_chance = 0.1 plural = @random.rand < plural_chance ? true : false untranslated = @random.rand < untranslated_chance ? true : false fuzzy = !untranslated && @random.rand < fuzzy_chance ? true : false multiline = @random.rand < multiline_chance ? true : false translator_comment = @random.rand < translator_comment_chance ? true : false extracted_comment = @random.rand < extracted_comment_chance ? true : false reference = @random.rand < reference_chance ? true : false multiple_reference = @random.rand < multiple_reference_chance ? true : false with_msgctxt = @random.rand < msgctxt_chance ? true : false msgctxt = with_msgctxt ? SecureRandom.base64((@random.rand*70.0).ceil) : nil if multiline lines = (@random.rand*4.0).ceil msgid = [] msgstr = [] for i in 0..lines msgid[i] = SecureRandom.base64((@random.rand*70.0).ceil) msgstr[i] = SecureRandom.base64((@random.rand*70.0).ceil) end else msgid = SecureRandom.base64((@random.rand*150.0).ceil) msgstr = SecureRandom.base64((@random.rand*150.0).ceil) end if plural if multiline msgid_plural = [] msgstr_plural = [] for i in 0..lines msgid_plural[i] = SecureRandom.base64((@random.rand*70.0).ceil) msgstr_plural[i] = SecureRandom.base64((@random.rand*70.0).ceil) end else msgid_plural = SecureRandom.base64((@random.rand*150.0).ceil) msgstr_plural = SecureRandom.base64((@random.rand*70.0).ceil) end end random_message += "# #{SecureRandom.base64((@random.rand*50.0).to_i)}\n" if translator_comment random_message += "#. #{SecureRandom.base64((@random.rand*50.0).to_i)}\n" if extracted_comment random_message += "#: #{SecureRandom.base64((@random.rand*50.0).to_i)}\n" if reference if multiple_reference references = (@random.rand*3.0).ceil for i in 0..references random_message += "#: #{SecureRandom.base64((@random.rand*50.0).to_i)}\n" end end if fuzzy random_message += "#, fuzzy\n" random_message += "##{"~" if obsolete}| msgctxt \"#{msgctxt}\"\n" if msgctxt if msgid.is_a?(Array) random_message += "##{"~" if obsolete}| msgid \"\"\n" msgid.each do |line| random_message += "##{"~" if obsolete}| \"#{line}\\n\"\n" end else random_message += "##{"~" if obsolete}| msgid \"#{msgid}\"\n" end if plural if msgid_plural.is_a?(Array) random_message += "##{"~" if obsolete}| msgid_plural \"\"\n" msgid_plural.each do |line| random_message += "##{"~" if obsolete}| \"#{line}\\n\"\n" end else random_message += "##{"~" if obsolete}| msgid_plural \"#{msgid_plural}\"\n" end end end random_message += "#{"#~ " if obsolete}msgctxt \"#{msgctxt}\"\n" if msgctxt if msgid.is_a?(Array) random_message += "#{"#~ " if obsolete}msgid \"\"\n" msgid.each do |line| random_message += "#{"#~ " if obsolete}\"#{line}\\n\"\n" end else random_message += "#{"#~ " if obsolete}msgid \"#{msgid}\"\n" end if plural if msgid_plural.is_a?(Array) random_message += "#{"#~ " if obsolete}msgid_plural \"\"\n" msgid_plural.each do |line| random_message += "#{"#~ " if obsolete}\"#{line}\\n\"\n" end else random_message += "#{"#~ " if obsolete}msgid_plural \"#{msgid_plural}\"\n" end if untranslated random_message += "#{"#~ " if obsolete}msgstr[0] \"\"\n" random_message += "#{"#~ " if obsolete}msgstr[1] \"\"\n" else if msgstr.is_a?(Array) random_message += "#{"#~ " if obsolete}msgstr[0] \"\"\n" msgstr.each do |line| random_message += "#{"#~ " if obsolete}\"#{line}\\n\"\n" end else random_message += "#{"#~ " if obsolete}msgstr[0] \"#{msgstr}\"\n" end if msgstr_plural.is_a?(Array) random_message += "#{"#~ " if obsolete}msgstr[1] \"\"\n" msgstr_plural.each do |line| random_message += "#{"#~ " if obsolete}\"#{line}\\n\"\n" end else random_message += "#{"#~ " if obsolete}msgstr[1] \"#{msgstr}\"\n" end end else if untranslated random_message += "#{"#~ " if obsolete}msgstr \"\"\n" else if msgstr.is_a?(Array) random_message += "#{"#~ " if obsolete}msgstr \"\"\n" msgstr.each do |line| random_message += "#{"#~ " if obsolete}\"#{line}\\n\"\n" end else random_message += "#{"#~ " if obsolete}msgstr \"#{msgstr}\"\n" end end end random_message += "\n" random_message end end end simple_po_parser-1.1.6/spec/spec_helper.rb0000644000004100000410000000200414217570357020671 0ustar www-datawww-data# This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # Require this file using `require "spec_helper"` to ensure that it is only # loaded once. # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration require 'simplecov' require 'coveralls' require 'awesome_print' SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] ) SimpleCov.start do add_group "gem", "lib" add_group "spec", "spec" end require 'simple_po_parser' RSpec.configure do |config| config.raise_errors_for_deprecations! config.run_all_when_everything_filtered = true config.filter_run :focus # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = 'random' end simple_po_parser-1.1.6/spec/simple_po_parser/0000755000004100000410000000000014217570357021422 5ustar www-datawww-datasimple_po_parser-1.1.6/spec/simple_po_parser/fixtures/0000755000004100000410000000000014217570357023273 5ustar www-datawww-datasimple_po_parser-1.1.6/spec/simple_po_parser/fixtures/header.po0000644000004100000410000000017014217570357025061 0ustar www-datawww-data# PO Header entry # #, fuzzy msgid "" msgstr "" "Project-Id-Version: simple_po_parser 1\n" "Report-Msgid-Bugs-To: me\n" simple_po_parser-1.1.6/spec/simple_po_parser/fixtures/multiline.po0000644000004100000410000000034614217570357025640 0ustar www-datawww-data#| msgid "multiline\n" #|"previous messageid" #|"with non-empty first line" msgid "" "multiline string " "with empty first line " "and trailing spaces" msgstr "multiline string" "with non-empty first line" "and no trailing spaces"simple_po_parser-1.1.6/spec/simple_po_parser/fixtures/crlf_encoded.po0000644000004100000410000000206514217570357026245 0ustar www-datawww-data# PO Header entry # #, fuzzy msgid "" msgstr "" "Project-Id-Version: simple_po_parser 1\n" "Report-Msgid-Bugs-To: me\n" "POT-Creation-Date: 2012-05-04 12:56+0000\n" "PO-Revision-Date: 2014-05-15 22:24+0330\n" "Last-Translator: Dennis-Florian Herr \n" "Language-Team: English\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" # translator-comment #. extract #: reference1 msgctxt "Context" msgid "msgid" msgstr "translated" # translator-comment # #. extract #: reference1 #: reference2 #, flag #| msgctxt "previous context" #| msgid "" #| "multiline\n" #|"previous messageid" #| msgid_plural "previous msgid_plural" msgctxt "Context" msgid "msgid" msgid_plural "" "multiline msgid_plural\n" "" msgstr[0] "msgstr 0" msgstr[1] "" "msgstr 1 multiline 1\n" "msgstr 1 line 2\n" msgstr[2] "msgstr 2" # translator-comment #. extract #: reference1 #~ msgctxt "Context" #~ msgid "msgid" #~ msgstr "translated" simple_po_parser-1.1.6/spec/simple_po_parser/fixtures/complete_file.po0000644000004100000410000000200214217570357026434 0ustar www-datawww-data# PO Header entry # #, fuzzy msgid "" msgstr "" "Project-Id-Version: simple_po_parser 1\n" "Report-Msgid-Bugs-To: me\n" "POT-Creation-Date: 2012-05-04 12:56+0000\n" "PO-Revision-Date: 2014-05-15 22:24+0330\n" "Last-Translator: Dennis-Florian Herr \n" "Language-Team: English\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" # translator-comment #. extract #: reference1 msgctxt "Context" msgid "msgid" msgstr "translated" # translator-comment # #. extract #: reference1 #: reference2 #, flag #| msgctxt "previous context" #| msgid "" #| "multiline\n" #|"previous messageid" #| msgid_plural "previous msgid_plural" msgctxt "Context" msgid "msgid" msgid_plural "" "multiline msgid_plural\n" "" msgstr[0] "msgstr 0" msgstr[1] "" "msgstr 1 multiline 1\n" "msgstr 1 line 2\n" msgstr[2] "msgstr 2" # translator-comment #. extract #: reference1 #~ msgctxt "Context" #~ msgid "msgid" #~ msgstr "translated" simple_po_parser-1.1.6/spec/simple_po_parser/fixtures/simple_entry.po0000644000004100000410000000014214217570357026342 0ustar www-datawww-data# translator-comment #. extract #: reference1 msgctxt "Context" msgid "msgid" msgstr "translated" simple_po_parser-1.1.6/spec/simple_po_parser/fixtures/non_ascii_file.po0000644000004100000410000000333514217570357026600 0ustar www-datawww-data# Persian translation for damned-lies. # Copyright (C) 2012 damned-lies's COPYRIGHT HOLDER # This file is distributed under the same license as the damned-lies package. # Arash Mousavi , 2014. # msgid "" msgstr "" "Project-Id-Version: damned-lies master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-05-04 12:56+0000\n" "PO-Revision-Date: 2014-05-15 22:24+0330\n" "Last-Translator: Arash Mousavi \n" "Language-Team: Persian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 1.6.4\n" #: database-content.py:1 database-content.py:129 settings.py:52 msgid "Afrikaans" msgstr "آفریقایی" #: templates/vertimus/vertimus_detail.html:105 #, python-format msgid " including %(stats)s image" msgid_plural " including %(stats)s images" msgstr[0] "" msgstr[1] "" #: templates/vertimus/vertimus_detail.html:136 vertimus/forms.py:79 msgid "Invalid action. Someone probably posted another action just before you." msgstr "" "فعالیت نامعتبر. شاید یک نفر دیگر دقیقا قبل از شما یک فعالیت دیگر ارسال کرده " "است." #: vertimus/models.py:470 #, python-format #| msgid "" #| "Hello,\n" #| "\n" #| "The new state of %(module)s - %(branch)s - %(domain)s (%(language)s) is " #| "now '%(new_state)s'.\n" #| "%(url)s\n" #| "\n" msgid "" "The new state of %(module)s - %(branch)s - %(domain)s (%(language)s) is now " "'%(new_state)s'." msgstr "" "وضعیت جدید %(module)s - %(branch)s - %(domain)s (%(language)s) هم‌اکنون " "«%(new_state)s» است." simple_po_parser-1.1.6/spec/simple_po_parser/fixtures/complex_entry.po0000644000004100000410000000056314217570357026527 0ustar www-datawww-data# translator-comment # #. extract #: reference1 #: reference2 #, flag #| msgctxt "previous context" #| msgid "" #| "multiline\n" #|"previous messageid" #| msgid_plural "previous msgid_plural" msgctxt "Context" msgid "msgid" msgid_plural "" "multiline msgid_plural\n" "" msgstr[0] "msgstr 0" msgstr[1] "" "msgstr 1 multiline 1\n" "msgstr 1 line 2\n" msgstr[2] "msgstr 2" simple_po_parser-1.1.6/spec/simple_po_parser/simple_po_parser_spec.rb0000644000004100000410000000156314217570357026331 0ustar www-datawww-datarequire "spec_helper" describe SimplePoParser do let(:po_file) { Pathname.new('spec/simple_po_parser/fixtures/complete_file.po').realpath } let(:crlf_file) { Pathname.new('spec/simple_po_parser/fixtures/crlf_encoded.po').realpath } let(:non_ascii_file) { Pathname.new('spec/simple_po_parser/fixtures/non_ascii_file.po').realpath } let(:po_complex_message) { File.read(File.expand_path("fixtures/complex_entry.po", __dir__))} it "parses a po file" do expect(SimplePoParser.parse(po_file)).to be_a_kind_of Array end it "parses crlf encoded files" do expect(SimplePoParser.parse(crlf_file)).to be_a_kind_of Array end it "parses a non ascii po file" do expect(SimplePoParser.parse(non_ascii_file)).to be_a_kind_of Array end it "parses a single message" do expect(SimplePoParser.parse_message(po_complex_message)).to be_a_kind_of Hash end end simple_po_parser-1.1.6/spec/simple_po_parser/parser_spec.rb0000644000004100000410000001506414217570357024263 0ustar www-datawww-datarequire "spec_helper" describe SimplePoParser::Parser do let (:po_header) { File.read(File.expand_path("fixtures/header.po", __dir__))} let(:po_complex_message) { File.read(File.expand_path("fixtures/complex_entry.po", __dir__))} let(:po_simple_message) { File.read(File.expand_path("fixtures/simple_entry.po", __dir__))} let(:po_multiline_message) { File.read(File.expand_path("fixtures/multiline.po", __dir__))} it "parses the PO header" do expected_result = { :translator_comment => ["PO Header entry", ""], :flag => "fuzzy", :msgid => "", :msgstr => ["", "Project-Id-Version: simple_po_parser 1\\n", "Report-Msgid-Bugs-To: me\\n"] } expect(SimplePoParser::Parser.new.parse(po_header)).to eq(expected_result) end it "parses the simple entry as expected" do expected_result = { :translator_comment => "translator-comment", :extracted_comment => "extract", :reference => "reference1", :msgctxt => "Context", :msgid => "msgid", :msgstr => "translated" } expect(SimplePoParser::Parser.new.parse(po_simple_message)).to eq(expected_result) end it "parses the multiline entry as expected" do expected_result = { :msgid => ["", "multiline string ", "with empty first line ", "and trailing spaces"], :msgstr => ["multiline string", "with non-empty first line", "and no trailing spaces"], :previous_msgid => ["multiline\\n", "previous messageid", "with non-empty first line"], } expect(SimplePoParser::Parser.new.parse(po_multiline_message)).to eq(expected_result) end it "parses the complex entry as expected" do expected_result = { :translator_comment => ["translator-comment", ""], :extracted_comment => "extract", :reference => ["reference1", "reference2"], :flag => "flag", :previous_msgctxt => "previous context", :previous_msgid => ["", "multiline\\n", "previous messageid"], :previous_msgid_plural => "previous msgid_plural", :msgctxt => "Context", :msgid => "msgid", :msgid_plural => ["", "multiline msgid_plural\\n", ""], "msgstr[0]" => "msgstr 0", "msgstr[1]" => ["", "msgstr 1 multiline 1\\n", "msgstr 1 line 2\\n"], "msgstr[2]" => "msgstr 2" } expect(SimplePoParser::Parser.new.parse(po_complex_message)).to eq(expected_result) end context "Errors" do it "errors cascade to ParserError" do message = "invalid message" expect{ SimplePoParser::Parser.new.parse(message) }.to raise_error(SimplePoParser::ParserError) end it "raises an error if there is no msgid" do message = "# comment\nmsgctxt \"ctxt\"\nmsgstr \"translation\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Message without msgid is not allowed/) end it "raises an error if there is no msgstr in singular message" do message = "# comment\nmsgctxt \"ctxt\"\nmsgid \"msg\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Singular message without msgstr is not allowed/) end it "raises an error if there is no msgstr[0] in plural message" do message = "# comment\nmsgid \"id\"\nmsgid_plural \"msg plural\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Plural message without msgstr\[0\] is not allowed/) message = "# comment\nmsgid \"id\"\nmsgid_plural \"msg plural\"\nmsgstr[1] \"plural trans\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Bad 'msgstr\[index\]' index/) end context "comments" do it "raises an error on unknown comment types" do message = "#- no such comment type" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Unknown comment type/) end it "raises an error on unknown previous comment types" do message = "#| msgstr \"no such comment type\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Previous comment type .*? unknown/) message = "#| bla " expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Previous comments must start with '#| msg'/) end it "raises an error if any lines are not marked obsolete after the first obsolete line" do message = "# comment\n#~msgid \"hi\"\nmsgstr \"should be obsolete\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /All lines must be obsolete after the first obsolete line, but got/) end it "raises an error if previous comments are not marked obsolete in obsolete entries" do message = "# comment\n#| msgid \"hi\"\n#~msgid \"hi\"\n#~msgstr \"should be obsolete\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Previous comment entries need to be marked obsolete too in obsolete message entries/) message = "# comment\n#| msgctxt \"hi\"\n#~msgid \"hi\"\n#~msgstr \"should be obsolete\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /Previous comment entries need to be marked obsolete too in obsolete message entries/) end end context "message_line" do it "raises an error if a message_line does not start with a double quote" do message = "msgid No starting double quote\"" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /A message text needs to start with the double quote character/) end it "raises an error if a message_line does not end with a double quote" do message = "msgid \"No ending double quote" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /The message text .*? must be finished with the double quote character/) end it "raises an error if there is anything but whitespace after the ending double quote" do message = "msgid \"text\" this shouldn't be here" expect{ SimplePoParser::Parser.new.parse(message) }. to raise_error(SimplePoParser::ParserError, /There should be only whitespace until the end of line after the double quote character/) end end end end simple_po_parser-1.1.6/CHANGELOG.md0000644000004100000410000000171214217570357016737 0ustar www-datawww-dataChangelog ============= ## Version 1.1.6 * fixes parsing of multiline strings that have content on the first line. Fixes issue #3, added via PR #4 by @mgruner ## Version 1.1.5 * added support for windows CRLF line endings. Fixes issue #2 * Note: CRLF support is enabled if the first line ends in a CRLF and reduces performance by about 50%. Performance for files only using \n is not affected. Files not using \r\n in the first line but somewhere else in the file might trigger errors. ## Version 1.1.4 * see 1.1.5. **Shouldn't be used**. ## Version 1.1.3 * merged PR#1 from @christopherstat for UTF-8 support of legacy ruby versions ## Version 1.1.2 * Made the parser thread-safe ## Version 1.1.2 * Made the parser thread-safe ## Version 1.1.0 * for line types only parsed once the parser returns a string instead of an array with one string ## Version 1.0.1 * added specs * fixed minor parser errors ## Version 1.0.0 * initial release simple_po_parser-1.1.6/simple_po_parser.gemspec0000644000004100000410000000174514217570357022044 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'simple_po_parser/version' Gem::Specification.new do |spec| spec.name = "simple_po_parser" spec.version = SimplePoParser::VERSION spec.authors = ["Dennis-Florian Herr"] spec.email = ["dennis.herr@experteer.com"] spec.summary = %q{A simple PO file to ruby hash parser} spec.description = %q{A simple PO file to ruby hash parser . PO files are translation files generated by GNU/Gettext tool.} spec.homepage = "http://github.com/experteer/simple_po_parser" spec.license = "MIT" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^spec/}) spec.require_paths = ["lib"] # Development deps spec.add_development_dependency "bundler", ">= 0" spec.add_development_dependency "rake", ">= 0" end simple_po_parser-1.1.6/.gitignore0000644000004100000410000000010314217570357017107 0ustar www-datawww-data*.gem .bundle .ruby-version Gemfile.lock coverage pkg test/tmp tmp simple_po_parser-1.1.6/.gitattributes0000644000004100000410000000006614217570357020022 0ustar www-datawww-dataspec/simple_po_parser/fixtures/crlf_encoded.po binary simple_po_parser-1.1.6/Rakefile0000644000004100000410000000414514217570357016576 0ustar www-datawww-data# encoding: utf-8 require 'rubygems' require 'rake' require 'rspec/core/rake_task' require 'bundler/gem_tasks' task :default => :spec desc "Generate a random po file to \"test/benchmark.po\". Takes optional rake args for number of entries" task 'generate_random_pofile', :messages, :obsoletes do |t, args| args.with_defaults(:messages => "200", :obsoletes => "10") require_relative 'spec/utils/random_pofile_generator' PoParser::RandomPoFileGenerator.generate_file( File.expand_path("test/benchmark.po", __dir__), args[:messages].to_i, args[:obsoletes].to_i ) end namespace :parser do require 'benchmark' require 'simple_po_parser' desc "Benchmark of 1000 full PoParser runs of test/benchmark.po" task "benchmark" do pofile = File.expand_path("test/benchmark.po", __dir__) Benchmark.bmbm do |x| x.report("Parser:") {1000.times { SimplePoParser.parse(pofile) }} end end desc "Generate 5 random PO files with 1000 to 5000 messages and benchmark each full PoParser run" task 'five_random_po_full' do include Benchmark require_relative 'spec/utils/random_pofile_generator' pofile = File.expand_path("test/benchmark.po.tmp", __dir__) Benchmark.benchmark(CAPTION, 6, FORMAT, "total:") do |x| total = nil total_length = 0 for i in 0..5 do length = (Random.new.rand * 4000.0 + 1000).to_i total_length += length puts "Benchmarking file of length #{length}" SimplePoParser::RandomPoFileGenerator.generate_file(pofile, length) t = x.report("try#{i}:") {SimplePoParser.parse(pofile)} File.unlink(pofile) total = total ? total+t : t end puts "Total message length #{total_length}" [total] end end desc "Show ruby-prof profiler for spec/fixtures/complex_entry.po" task "profile_parser" do require 'ruby-prof' po_message = File.read(File.expand_path("spec/simple_po_parser/fixtures/complex_entry.po", __dir__)) RubyProf.start SimplePoParser.parse_message(po_message) result = RubyProf.stop printer = RubyProf::FlatPrinter.new(result) printer.print(STDOUT) end end simple_po_parser-1.1.6/lib/0000755000004100000410000000000014217570357015673 5ustar www-datawww-datasimple_po_parser-1.1.6/lib/simple_po_parser.rb0000644000004100000410000000070214217570357021562 0ustar www-datawww-data# encoding: utf-8 require 'simple_po_parser/error' require 'simple_po_parser/parser' require 'simple_po_parser/tokenizer' require 'simple_po_parser/version' module SimplePoParser class << self # parse po file # # returns an array of po messages as hashes def parse(path) Tokenizer.new.parse_file(path) end # parses a single message. def parse_message(message) Parser.new.parse(message) end end end simple_po_parser-1.1.6/lib/simple_po_parser/0000755000004100000410000000000014217570357021236 5ustar www-datawww-datasimple_po_parser-1.1.6/lib/simple_po_parser/version.rb0000644000004100000410000000010114217570357023240 0ustar www-datawww-data# encoding: utf-8 module SimplePoParser VERSION = "1.1.6" end simple_po_parser-1.1.6/lib/simple_po_parser/tokenizer.rb0000644000004100000410000000125614217570357023601 0ustar www-datawww-data# encoding: utf-8 module SimplePoParser # Split a PO file into single PO message entities (a message is seperated by two newline) class Tokenizer def initialize @messages = [] end def parse_file(path) file = File.open(path, "r") if(file.gets =~ /\r$/) # detected windows line ending file.close file = File.open(path, "rt") else file.rewind end file.each_line("\n\n") do |block| block.strip! # dont parse empty blocks @messages << parse_block(block) if block != '' end @messages end private def parse_block(block) Parser.new.parse(block) end end end simple_po_parser-1.1.6/lib/simple_po_parser/error.rb0000644000004100000410000000032714217570357022716 0ustar www-datawww-data# encoding: utf-8 module SimplePoParser class ParserError < StandardError end class PoSyntaxError < ParserError @msg = "" def initialize(msg="Invalid po syntax") @msg = msg super(msg) end end end simple_po_parser-1.1.6/lib/simple_po_parser/parser.rb0000644000004100000410000003224614217570357023066 0ustar www-datawww-data# encoding: utf-8 module SimplePoParser # Fast parser directly using Rubys powerful StringScanner (strscan) # # Important notes about StringScanner.scan: # * scan will return nil if there is no match. Using the regex * (zero or more) quantifier will # let scan return an empty string if there is "no match" as the empty string qualifies as # a match of the regex (zero times). We make use of this "trick" # * the start of line anchor ^ is obsolete as scan will only match start of line. # * rubys regex is by default in single-line mode, therefore scan will only match until # the next newline is hit (unless multi-line mode is explicitly enabled) class Parser require_relative 'error' require 'strscan' # parse a single message of the PO format. # # @param message a single PO message in String format without leading or trailing whitespace # @return [Hash] parsed PO message information in Hash format def parse(message) @result = {} @scanner = StringScanner.new(message.strip) begin lines rescue ParserError => pe error_msg = "SimplePoParser::ParserError" error_msg += pe.message error_msg += "\nParseing result before error: '#{@result}'" error_msg += "\nSimplePoParser filtered backtrace: SimplePoParser::ParserError" backtrace = "#{pe.backtrace.select{|i| i =~ /lib\/simple_po_parser/}.join("\n\tfrom ")}" raise ParserError, error_msg, backtrace end @result end private ######################################### ### branching ### ######################################### # arbitary line of a PO message. Can be comment or message # message parsing is always started with checking for msgctxt as content is expected in # msgctxt -> msgid -> msgid_plural -> msgstr order def lines begin if @scanner.scan(/#/) comment else msgctxt end rescue PoSyntaxError => pe # throw a normal ParserError to break the recursion raise ParserError, "Syntax error in lines\n" + pe.message, pe.backtrace end end # match a comment line. called on lines starting with '#'. # Recalls line when the comment line was parsed def comment begin case @scanner.getch when ' ' skip_whitespace add_result(:translator_comment, comment_text) lines when '.' skip_whitespace add_result(:extracted_comment, comment_text) lines when ':' skip_whitespace add_result(:reference, comment_text) lines when ',' skip_whitespace add_result(:flag, comment_text) lines when '|' skip_whitespace previous_comments lines when "\n" add_result(:translator_comment, "") # empty comment line lines when '~' if @result[:previous_msgctxt] || @result[:previous_msgid] || @result[:previous_msgid_plural] raise PoSyntaxError, "Previous comment entries need to be marked obsolete too in obsolete message entries. But already got: #{@result}" end skip_whitespace add_result(:obsolete, comment_text) obsoletes else @scanner.pos = @scanner.pos - 2 raise PoSyntaxError, "Unknown comment type #{@scanner.peek(10).inspect}" end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in comment\n" + pe.message, pe.backtrace end end # matches the msgctxt line and will continue to check for msgid afterwards # # msgctxt is optional def msgctxt begin if @scanner.scan(/msgctxt/) skip_whitespace text = message_line add_result(:msgctxt, text) message_multiline(:msgctxt) if @scanner.peek(1) == '"' end msgid rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in msgctxt\n" + pe.message, pe.backtrace end end # matches the msgid line. Will check for optional msgid_plural. # Will advance to msgstr or msgstr_plural based on msgid_plural # # msgid is required def msgid begin if @scanner.scan(/msgid/) skip_whitespace text = message_line add_result(:msgid, text) message_multiline(:msgid) if @scanner.peek(1) == '"' if msgid_plural msgstr_plural else msgstr end else err_msg = "Message without msgid is not allowed." err_msg += "The Line started unexpectedly with #{@scanner.peek(10).inspect}." raise PoSyntaxError, err_msg end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in msgid\n" + pe.message, pe.backtrace end end # matches the msgid_plural line. # # msgid_plural is optional # # @return [boolean] true if msgid_plural is present, false otherwise def msgid_plural begin if @scanner.scan(/msgid_plural/) skip_whitespace text = message_line add_result(:msgid_plural, text) message_multiline(:msgid_plural) if @scanner.peek(1) == '"' true else false end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in msgid\n" + pe.message, pe.backtrace end end # parses the msgstr singular line # # msgstr is required in singular translations def msgstr begin if @scanner.scan(/msgstr/) skip_whitespace text = message_line add_result(:msgstr, text) message_multiline(:msgstr) if @scanner.peek(1) == '"' skip_whitespace raise PoSyntaxError, "Unexpected content after expected message end #{@scanner.peek(10).inspect}" unless @scanner.eos? else raise PoSyntaxError, "Singular message without msgstr is not allowed. Line started unexpectedly with #{@scanner.peek(10).inspect}." end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in msgstr\n" + pe.message, pe.backtrace end end # parses the msgstr plural lines # # msgstr plural lines are used when there is msgid_plural. # They have the format msgstr[N] where N is incremental number starting from zero representing # the plural number as specified in the headers "Plural-Forms" entry. Most languages, like the # English language only have two plural forms (singular and plural), # but there are languages with more plurals def msgstr_plural(num = 0) begin msgstr_key = @scanner.scan(/msgstr\[\d\]/) # matches 'msgstr[0]' to 'msgstr[9]' if msgstr_key # msgstr plurals must come in 0-based index in order msgstr_num = msgstr_key.match(/\d/)[0].to_i raise PoSyntaxError, "Bad 'msgstr[index]' index." if msgstr_num != num skip_whitespace text = message_line add_result(msgstr_key, text) message_multiline(msgstr_key) if @scanner.peek(1) == '"' msgstr_plural(num+1) elsif num == 0 # and msgstr_key was false raise PoSyntaxError, "Plural message without msgstr[0] is not allowed. Line started unexpectedly with #{@scanner.peek(10).inspect}." else raise PoSyntaxError, "End of message was expected, but line started unexpectedly with #{@scanner.peek(10).inspect}" unless @scanner.eos? end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in msgstr_plural\n" + pe.message, pe.backtrace end end # parses previous comments, which provide additional information on fuzzy matching # # previous comments are: # * #| msgctxt # * #| msgid # * #| msgid_plural def previous_comments begin # next part must be msgctxt, msgid or msgid_plural if @scanner.scan(/msg/) if @scanner.scan(/id/) if @scanner.scan(/_plural/) key = :previous_msgid_plural else key = :previous_msgid end elsif @scanner.scan(/ctxt/) key = :previous_msgctxt else raise PoSyntaxError, "Previous comment type #{("msg" + @scanner.peek(10)).inspect} unknown." end skip_whitespace text = message_line add_result(key, text) previous_multiline(key) if @scanner.match?(/#\|\p{Blank}*"/) else raise PoSyntaxError, "Previous comments must start with '#| msg'. #{@scanner.peek(10).inspect} unknown." end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in previous_comments\n" + pe.message, pe.backtrace end end # parses the multiline messages of the previous comment lines def previous_multiline(key) begin # scan multilines until no further multiline is hit # /#\|\p{Blank}"/ needs to catch the double quote to ensure it hits a previous # multiline and not another line type. if @scanner.scan(/#\|\p{Blank}*"/) @scanner.pos = @scanner.pos - 1 # go one character back, so we can reuse the "message line" method add_result(key, message_line) previous_multiline(key) # go on until we no longer hit a multiline line end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in previous_multiline\n" + pe.message, pe.backtrace end end # parses a multiline message # # Multiline messages are usually indicated by an empty string as the first line, # followed by more lines starting with the double quote character. # # However, according to the PO file standard, the first line can also contain content. def message_multiline(key) begin skip_whitespace if @scanner.check(/"/) add_result(key, message_line) message_multiline(key) end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in message_multiline with key '#{key}'\n" + pe.message, pe.backtrace end end # identifies a message line and returns it's text or raises an error # # @return [String] message_text def message_line begin if @scanner.getch == '"' text = message_text unless @scanner.getch == '"' err_msg = "The message text '#{text}' must be finished with the double quote character '\"'." raise PoSyntaxError, err_msg end skip_whitespace unless end_of_line err_msg = "There should be only whitespace until the end of line" err_msg += " after the double quote character of a message text." raise PoSyntaxError.new(err_msg) end text else @scanner.pos = @scanner.pos - 1 err_msg = "A message text needs to start with the double quote character '\"'," err_msg += " but this was found: #{@scanner.peek(10).inspect}" raise PoSyntaxError, err_msg end rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in message_line\n" + pe.message, pe.backtrace end end # parses all obsolete lines. # An obsolete message may only contain obsolete lines def obsoletes if @scanner.scan(/#~/) skip_whitespace add_result(:obsolete, comment_text) obsoletes else raise PoSyntaxError, "All lines must be obsolete after the first obsolete line, but got #{@scanner.peek(10).inspect}." unless @scanner.eos? end end ######################################### ### scanning ### ######################################### # returns the text of a comment # # @return [String] text def comment_text begin text = @scanner.scan(/.*/) # everything until newline text.rstrip! # benchmarked faster too rstrip the string in place raise PoSyntaxError, "Comment text should advance to next line or stop at eos" unless end_of_line text rescue PoSyntaxError => pe raise PoSyntaxError, "Syntax error in commtent_text\n" + pe.message, pe.backtrace end end # returns the text of a message line # # @return [String] text def message_text @scanner.scan_until(/(\\(\\|")|[^"])*/) # this parses anything until an unescaped quote is hit end # advances the scanner until the next non whitespace position. # Does not match newlines. See WHITESPACE_REGEX constant def skip_whitespace @scanner.skip(/\p{Blank}+/) end # returns true if the scanner is at beginning of next line or end of string # # @return [Boolean] true if scanner at beginning of line or eos def end_of_line @scanner.scan(/\n/) @scanner.eos? || @scanner.bol? end # adds text to the given key in results # creates an array if the given key already has a result def add_result(key, text) if @result[key] if @result[key].is_a? Array @result[key].push(text) else @result[key] = [@result[key], text] end else @result[key] = text end end end end simple_po_parser-1.1.6/Gemfile0000644000004100000410000000024214217570357016416 0ustar www-datawww-datasource 'https://rubygems.org' group :test do gem 'coveralls', :require => false gem 'rspec', '~> 3.5.0' gem 'awesome_print' gem 'ruby-prof' end gemspec simple_po_parser-1.1.6/LICENSE.txt0000644000004100000410000000210514217570357016746 0ustar www-datawww-dataCopyright (c) 2017 Dennis-Florian Herr @ Experteer GmbH MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.