1 /* 2 gettext .mo support 3 */ 4 module i18n.mo; 5 import i18n.tr; 6 import std.format; 7 import std.exception; 8 import std.string : fromStringz; 9 import std.bitmanip : swapEndian; 10 11 private: 12 struct MOEntry { 13 align(1): 14 uint length; 15 uint offset; 16 } 17 18 struct MOHeader { 19 align(1): 20 uint magic; 21 uint revision; 22 uint count; 23 uint sourceOffset; 24 uint targetOffset; 25 uint hashSize; 26 void* hashes; 27 } 28 29 enum MAGIC_SWAB = 0xde120495; 30 enum MAGIC = 0x950412de; 31 32 pragma(inline, true) 33 uint endian(MOFile* f, uint x) { return ((f.header.magic == MAGIC_SWAB) ? swapEndian(x) : (x)); } 34 35 // bool mo_init = false; 36 // ubyte[] mo_data; 37 // MOFile mo; 38 39 public: 40 41 struct MOFile { 42 private: 43 ubyte[] data; 44 45 MOHeader header; 46 MOEntry* sources; 47 MOEntry* targets; 48 } 49 50 MOFile* i18nMOLoad(ubyte[] data) { 51 if (data.length == 0) return null; 52 53 MOFile* mo = new MOFile; 54 mo.data = data; 55 56 (cast(void*)&mo.header)[0..MOHeader.sizeof] = data[0..MOHeader.sizeof]; 57 if (mo.header.magic == MAGIC_SWAB) { 58 mo.header.revision = swapEndian(mo.header.revision); 59 mo.header.count = swapEndian(mo.header.count); 60 mo.header.sourceOffset = swapEndian(mo.header.sourceOffset); 61 mo.header.targetOffset = swapEndian(mo.header.targetOffset); 62 mo.header.hashSize = swapEndian(mo.header.hashSize); 63 mo.header.hashes = cast(void*)swapEndian(cast(size_t)mo.header.magic); 64 } 65 66 enforce(mo.header.magic == MAGIC, "Bad mo file magic 0x%08x".format(mo.header.magic)); 67 enforce(mo.header.revision == 0, "Bad mo file revision 0x%08x".format(mo.header.revision)); 68 69 mo.sources = cast(MOEntry*)(mo.data.ptr+mo.header.sourceOffset); 70 mo.targets = cast(MOEntry*)(mo.data.ptr+mo.header.targetOffset); 71 72 return mo; 73 } 74 75 string i18nMOStr(MOFile* mo, string str) { 76 77 // No data was found 78 if (!mo.data) return str; 79 80 foreach(i; 0..mo.header.count) { 81 if (str == cast(string)mo.data[mo.sources[i].offset..mo.endian(mo.sources[i].offset)+mo.endian(mo.sources[i].length)]) { 82 83 // Empty translation? return base string. 84 if (mo.targets[i].length == 0) return str; 85 86 // Return the correct translation (pluralization 0) 87 return cast(string)mo.data[mo.targets[i].offset..mo.endian(mo.targets[i].offset)+mo.endian(mo.targets[i].length)]; 88 } 89 } 90 91 return str; 92 } 93 94 const(char)* i18nMOStrC(MOFile* mo, string str) { 95 import std.string : toStringz; 96 97 // No data was found 98 if (!mo.data) return str.toStringz; 99 100 foreach(i; 0..mo.header.count) { 101 if (str == cast(string)mo.data[mo.sources[i].offset..mo.endian(mo.sources[i].offset)+mo.endian(mo.sources[i].length)]) { 102 103 // Empty translation? return base string. 104 if (mo.targets[i].length == 0) return str.toStringz; 105 106 // Return the correct translation (pluralization 0) 107 return cast(const(char)*)&mo.data[mo.targets[i].offset]; 108 } 109 } 110 111 return str.toStringz; 112 } 113 114 TREntry[string] i18nMOGenStrings(MOFile* mo) { 115 import std.string : toStringz; 116 TREntry[string] entries; 117 118 foreach(i; 0..mo.header.count) { 119 120 // Skip untranslated entries 121 if (mo.targets[i].length == 0) continue; 122 123 // Add translation to translation table 124 // TODO: Add pluralizations 125 string source = cast(string)mo.data[mo.sources[i].offset..mo.endian(mo.sources[i].offset)+mo.endian(mo.sources[i].length)]; 126 string target0 = cast(string)mo.data[mo.targets[i].offset..mo.endian(mo.targets[i].offset)+mo.endian(mo.targets[i].length)]; 127 entries[source] = TREntry(source, [target0], [target0.toStringz]); 128 } 129 130 return entries; 131 }