{"id":16696,"date":"2012-09-10T16:06:12","date_gmt":"2012-09-10T14:06:12","guid":{"rendered":"https:\/\/mamchenkov.net\/wordpress\/?p=16696"},"modified":"2012-09-10T16:06:12","modified_gmt":"2012-09-10T14:06:12","slug":"managing-gettext-translations-on-the-command-line","status":"publish","type":"post","link":"https:\/\/mamchenkov.net\/wordpress\/2012\/09\/10\/managing-gettext-translations-on-the-command-line\/","title":{"rendered":"Managing gettext translations on the command line"},"content":{"rendered":"<!-- google_ad_section_start -->\n<p>I am working on a rather multilingual project in the office currently. \u00a0And, as always, we tried a few alternatives before ending up with gettext again. \u00a0For those of you who don&#8217;t know, <a href=\"http:\/\/www.gnu.org\/software\/gettext\/\">gettext<\/a> is the de facto standard for managing language translations in software, especially when it comes to messages and user interface elements. \u00a0It&#8217;s a nice, powerful system but it&#8217;s a bit awkward when things come to web development.<\/p>\n<p>Anyways, we started using it in a bit of a rush, without doing all the necessary planning, and quite soon ended up in a bit of a mess. \u00a0Different people used different editors to update translations. \u00a0And each person&#8217;s environment was setup in a different way. \u00a0 All that made its way into the PO files that hold translations. \u00a0More so, we didn&#8217;t really define the procedure for the updates of translations. \u00a0That became a bigger problem when we realized that Arabic has only 50 translated strings, while English has 220, and Chinese 350. \u00a0All languages were supposed to have exactly the same amount of strings, even if the actual translations were missing.<\/p>\n<p>So today I had to rethink and redefine how we do it. \u00a0First of all, I had to figure out and try the process outside of the project. \u00a0It took me a good couple of hours to brush up my gettext knowledge and find some useful documentation online. \u00a0Here is a very helpful <a href=\"http:\/\/oriya.sarovar.org\/docs\/gettext_single.html\">article that got me started<\/a>.<\/p>\n<p>After reading the article, a few manuals and playing with the actual commands, I decided on the following:<\/p>\n<ol>\n<li>The source of all translations will be a single POT file. \u00a0This file will be completely dropped and regenerated every time any strings are updated in the source code.<\/li>\n<li>Each language will have a PO file of its own. However, the strings for the language won&#8217;t be extracted from the source code, but from the common POT file.<\/li>\n<li>All editors will use current project folder as the primary path. \u00a0In other words, &#8220;.&#8221; instead of full path to &#8220;\/var\/www\/foobar&#8221;. \u00a0This will make all file references in PO\/POT files point to a relative location to the project folder, ignoring the specifics of each contributor&#8217;s setup.<\/li>\n<li>Updating language template files (PO) and building of MO files will be a part of the project build\/deploy script, to make sure everything stays as up to date as possible.<\/li>\n<\/ol>\n<p>Now for the actual code. \u00a0 Here is the shell script that does the job. (Here is a link to the\u00a0<a href=\"https:\/\/gist.github.com\/3690981\">Gist<\/a>, just in case I&#8217;ll update it in the future.)<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n#!\/bin\/bash\r\n\r\nDOMAIN=&quot;project_tag&quot;\r\nPOT=&quot;$DOMAIN.pot&quot;\r\nLANGS=&quot;en_US ru_RU&quot;\r\nSOURCES=&quot;*.php&quot;\r\n\r\n# Create template\r\necho &quot;Creating POT&quot;\r\nrm -f $POT\r\nxgettext \\\r\n --copyright-holder=&quot;2012 My Company Ltd&quot; \\\r\n --package-name=&quot;Project Name&quot; \\\r\n --package-version=&quot;1.0&quot; \\\r\n --msgid-bugs-address=&quot;translations@company.com&quot; \\\r\n --language=PHP \\\r\n --sort-output \\\r\n --keyword=__ \\\r\n --keyword=_e \\\r\n --from-code=UTF-8 \\\r\n --output=$POT \\\r\n --default-domain=$DOMAIN \\\r\n $SOURCES\r\n\r\n# Create languages\r\nfor LANG in $LANGS\r\ndo\r\n if &#x5B; ! -e &quot;$LANG.po&quot; ]\r\n then\r\n echo &quot;Creating language file for $LANG&quot;\r\n msginit --no-translator --locale=$LANG.UTF-8 --output-file=$LANG.po --input=$POT\r\n fi\r\n\r\necho &quot;Updating language file for $LANG from $POT&quot;\r\n msgmerge --sort-output --update --backup=off $LANG.po $POT\r\n\r\necho &quot;Converting $LANG.po to $LANG.mo&quot;\r\n msgfmt --check --verbose --output-file=$LANG.mo $LANG.po\r\ndone\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Now, all you need to do is run the script once to get the default POT file and a PO file for every language. \u00a0You can edit PO files with translations for as much as you want. \u00a0Then simply run the script again and it will update generated MO files. \u00a0No parameters, no manuals, no nothing. \u00a0If you need to add another language, just put the appropriate locale in the $LANGS variable and run the script again. \u00a0You are good to go.<\/p>\n<p>Enjoy!<\/p>\n<!-- google_ad_section_end -->\n","protected":false},"excerpt":{"rendered":"<!-- google_ad_section_start -->\n<p>I am working on a rather multilingual project in the office currently. \u00a0And, as always, we tried a few alternatives before ending up with gettext again. \u00a0For those of you who don&#8217;t know, gettext is the de facto standard for managing language translations in software, especially when it comes to messages and user interface elements. &hellip; <a href=\"https:\/\/mamchenkov.net\/wordpress\/2012\/09\/10\/managing-gettext-translations-on-the-command-line\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Managing gettext translations on the command line<\/span><\/a><\/p>\n<!-- google_ad_section_end -->\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_links_to":"","_links_to_target":""},"categories":[1,18,62,1334,60],"tags":[3098,1960,1478,1279,17],"keyring_services":[],"class_list":["post-16696","post","type-post","status-publish","format-standard","hentry","category-general","category-programming","category-technology","category-web-work","category-wordpress","tag-bash","tag-command-line","tag-localization","tag-translations","tag-work"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":15831,"url":"https:\/\/mamchenkov.net\/wordpress\/2011\/11\/01\/day-in-brief-2011-11-01\/","url_meta":{"origin":16696,"position":0},"title":"Day in brief &#8211; 2011-11-01","author":"Leonid Mamchenkov","date":"November 1, 2011","format":false,"excerpt":"New #GoogleReader is being deployed. I got a 'Welcome' screen and now it 'Takes too long'. I can wait, I guess. # Auditors are funny. Suits, ties, notepads, pens. Walking around our office. This is IT. Check. This is marketing. Check. This is .. Check. # I'm not a big\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":7630,"url":"https:\/\/mamchenkov.net\/wordpress\/2004\/07\/01\/how-things-get-lost-in-translations\/","url_meta":{"origin":16696,"position":1},"title":"How things get lost in translations","author":"Leonid Mamchenkov","date":"July 1, 2004","format":false,"excerpt":"Translating from one language into another is not an easy job. Translating from one language into another without losing some part of the meaning is an impossible job. There are few films and stories illustrating these difficulties. One of them, of course, is \"Lost in translation\". If you need another\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":10988,"url":"https:\/\/mamchenkov.net\/wordpress\/2007\/12\/19\/simpler-google-talk-translations\/","url_meta":{"origin":16696,"position":2},"title":"Simpler Google Talk translations?","author":"Leonid Mamchenkov","date":"December 19, 2007","format":false,"excerpt":"Google has recently added Gtalk bots that can do translations to various languages, mostly available with Google Translate.\u00c2\u00a0 While I'm all for helping people understand each other better (even though there are certain complains regarding the quality of translation), I think this functionality could have implemented simpler. Disclaimer: I haven't\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":7689,"url":"https:\/\/mamchenkov.net\/wordpress\/2004\/07\/19\/vladimir-vysotsky-translated\/","url_meta":{"origin":16696,"position":3},"title":"Vladimir Vysotsky translated","author":"Leonid Mamchenkov","date":"July 19, 2004","format":false,"excerpt":"It is not a secret that it is almost impossible to translate anything more complex then 'Hello' from one language to another. Translating poems is often considered to be the most difficult tasks. That is, probably, why we don't see good translations of poems so often. Today I came across\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":9036,"url":"https:\/\/mamchenkov.net\/wordpress\/2005\/06\/02\/new-default-dictionary\/","url_meta":{"origin":16696,"position":4},"title":"New default dictionary","author":"Leonid Mamchenkov","date":"June 2, 2005","format":false,"excerpt":"Until now I've been using http:\/\/www.rambler.ru\/dict for all my translation needs. I realize that it might not at all be the best out there, but it is was good enough. I wanted something fast and simple. No need for phrases, just quick word translations from Russian to English, and back.\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":27070,"url":"https:\/\/mamchenkov.net\/wordpress\/2016\/12\/05\/wordpress-preferred-languages-research\/","url_meta":{"origin":16696,"position":5},"title":"WordPress : Preferred Languages Research","author":"Leonid Mamchenkov","date":"December 5, 2016","format":false,"excerpt":"Pascal Birchler of the WordPress blogs some interesting research he did in the area of handling preferred language and how different systems - ranging from browsers, wikis, and social networks to all kinds of content management systems - approach and solve the problem. Drupal Drupal 8 has a rather powerful\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"drupal-language-hierarchy-module","src":"https:\/\/i0.wp.com\/mamchenkov.net\/wordpress\/wp-content\/uploads\/2016\/12\/drupal-language-hierarchy-module-500x155.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]}],"jetpack_sharing_enabled":true,"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts\/16696","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/comments?post=16696"}],"version-history":[{"count":0,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts\/16696\/revisions"}],"wp:attachment":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/media?parent=16696"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/categories?post=16696"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/tags?post=16696"},{"taxonomy":"keyring_services","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/keyring_services?post=16696"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}