generate-mo.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <?php
  2. /**
  3. * Generate .mo files from .po files.
  4. * Run: php generate-mo.php
  5. * Or use Poedit / WP-CLI: wp i18n make-mo .
  6. */
  7. if (php_sapi_name() !== 'cli') {
  8. die('CLI only');
  9. }
  10. $dir = __DIR__;
  11. $po_files = glob($dir . '/*.po');
  12. foreach ($po_files as $po_file) {
  13. $mo_file = preg_replace('/\.po$/', '.mo', $po_file);
  14. echo "Converting: " . basename($po_file) . " -> " . basename($mo_file) . "\n";
  15. $entries = parse_po($po_file);
  16. write_mo($mo_file, $entries);
  17. echo "Done. " . count($entries) . " entries.\n";
  18. }
  19. function parse_po($file) {
  20. $content = file_get_contents($file);
  21. $entries = array();
  22. $current_msgid = null;
  23. $current_msgstr = array();
  24. $current_msgid_plural = null;
  25. $state = '';
  26. $lines = explode("\n", $content);
  27. foreach ($lines as $line) {
  28. $line = trim($line);
  29. // Skip comments
  30. if (empty($line) || $line[0] === '#') {
  31. if ($current_msgid !== null && $state !== '') {
  32. save_entry($entries, $current_msgid, $current_msgid_plural, $current_msgstr);
  33. $current_msgid = null;
  34. $current_msgid_plural = null;
  35. $current_msgstr = array();
  36. $state = '';
  37. }
  38. continue;
  39. }
  40. if (preg_match('/^msgid\s+"(.*)"$/', $line, $m)) {
  41. if ($current_msgid !== null) {
  42. save_entry($entries, $current_msgid, $current_msgid_plural, $current_msgstr);
  43. }
  44. $current_msgid = stripcslashes($m[1]);
  45. $current_msgid_plural = null;
  46. $current_msgstr = array();
  47. $state = 'msgid';
  48. } elseif (preg_match('/^msgid_plural\s+"(.*)"$/', $line, $m)) {
  49. $current_msgid_plural = stripcslashes($m[1]);
  50. $state = 'msgid_plural';
  51. } elseif (preg_match('/^msgstr\s+"(.*)"$/', $line, $m)) {
  52. $current_msgstr[0] = stripcslashes($m[1]);
  53. $state = 'msgstr';
  54. } elseif (preg_match('/^msgstr\[(\d+)\]\s+"(.*)"$/', $line, $m)) {
  55. $current_msgstr[(int)$m[1]] = stripcslashes($m[2]);
  56. $state = 'msgstr[' . $m[1] . ']';
  57. } elseif (preg_match('/^"(.*)"$/', $line, $m)) {
  58. $val = stripcslashes($m[1]);
  59. if ($state === 'msgid') {
  60. $current_msgid .= $val;
  61. } elseif ($state === 'msgid_plural') {
  62. $current_msgid_plural .= $val;
  63. } elseif (preg_match('/^msgstr/', $state)) {
  64. $idx = 0;
  65. if (preg_match('/\[(\d+)\]/', $state, $im)) {
  66. $idx = (int)$im[1];
  67. }
  68. $current_msgstr[$idx] = ($current_msgstr[$idx] ?? '') . $val;
  69. }
  70. }
  71. }
  72. if ($current_msgid !== null) {
  73. save_entry($entries, $current_msgid, $current_msgid_plural, $current_msgstr);
  74. }
  75. return $entries;
  76. }
  77. function save_entry(&$entries, $msgid, $msgid_plural, $msgstr) {
  78. if ($msgid === null) return;
  79. if ($msgid_plural !== null) {
  80. $key = $msgid . "\x00" . $msgid_plural;
  81. ksort($msgstr);
  82. $val = implode("\x00", $msgstr);
  83. } else {
  84. $key = $msgid;
  85. $val = $msgstr[0] ?? '';
  86. }
  87. $entries[$key] = $val;
  88. }
  89. function write_mo($file, $entries) {
  90. ksort($entries);
  91. $offsets = array();
  92. $ids = '';
  93. $strs = '';
  94. foreach ($entries as $id => $str) {
  95. $offsets[] = array(strlen($id), strlen($ids), strlen($str), strlen($strs));
  96. $ids .= $id . "\x00";
  97. $strs .= $str . "\x00";
  98. }
  99. $count = count($offsets);
  100. $header_size = 28;
  101. $key_offset_start = $header_size;
  102. $val_offset_start = $key_offset_start + $count * 8;
  103. $keys_start = $val_offset_start + $count * 8;
  104. $vals_start = $keys_start + strlen($ids);
  105. $output = pack('V', 0x950412de); // magic
  106. $output .= pack('V', 0); // revision
  107. $output .= pack('V', $count); // number of strings
  108. $output .= pack('V', $key_offset_start); // offset of original strings table
  109. $output .= pack('V', $val_offset_start); // offset of translated strings table
  110. $output .= pack('V', 0); // hash table size
  111. $output .= pack('V', 0); // hash table offset
  112. // Original strings offsets
  113. foreach ($offsets as $o) {
  114. $output .= pack('V', $o[0]); // length
  115. $output .= pack('V', $keys_start + $o[1]); // offset
  116. }
  117. // Translated strings offsets
  118. foreach ($offsets as $o) {
  119. $output .= pack('V', $o[2]); // length
  120. $output .= pack('V', $vals_start + $o[3]); // offset
  121. }
  122. $output .= $ids;
  123. $output .= $strs;
  124. file_put_contents($file, $output);
  125. }