1 module hunt.framework.i18n.I18n; 2 3 import std.path; 4 import std.file; 5 import std.algorithm; 6 import std.array; 7 import std.stdio; 8 import std.string; 9 import std.json; 10 11 import hunt.logging; 12 13 alias StrStrStr = string[string][string]; 14 15 enum I18N_DEFAULT_LOCALE = "en-us"; 16 17 /** 18 * 19 */ 20 class I18n { 21 private { 22 StrStrStr _res; 23 // __gshared I18n _instance; 24 string _default; 25 } 26 27 this() { 28 _default = I18N_DEFAULT_LOCALE; 29 } 30 31 // static I18n instance() { 32 // if (_instance is null) { 33 // _instance = new I18n(); 34 // } 35 // return _instance; 36 // } 37 38 bool loadLangResources(string path, lazy string ext = "ini") { 39 _isResLoaded = false; 40 if(!path.exists()) { 41 warningf("The resource does not exist: %s", path); 42 return false; 43 } 44 auto resfiles = std.file.dirEntries(path, "*.{" ~ ext ~ "}", SpanMode.depth).filter!(a => a.isFile) 45 .map!(a => std.path.absolutePath(a.name)) 46 .array; 47 if (resfiles.length == 0) { 48 logDebug("The lang resource file is empty"); 49 return false; 50 } 51 52 foreach (r; resfiles) { 53 parseResFile(r); 54 } 55 _isResLoaded = true; 56 return true; 57 } 58 59 bool clear() 60 { 61 this._res = null; 62 return false; 63 } 64 65 bool clear(string local) 66 { 67 this._res[local] = null; 68 return false; 69 } 70 71 I18n add(string local, string key, string value) 72 { 73 this._res[local][key] = value; 74 return this; 75 } 76 77 I18n merge(string local, string[string] data) 78 { 79 foreach(string key, string value; data) { 80 this._res[local][key] = value; 81 } 82 return this; 83 } 84 85 @property bool isResLoaded() { 86 return _isResLoaded; 87 } 88 89 private bool _isResLoaded = false; 90 91 @property StrStrStr resources() { 92 return this._res; 93 } 94 95 @property defaultLocale(string loc) { 96 this._default = loc; 97 } 98 99 @property string defaultLocale() { 100 return this._default; 101 } 102 103 private bool parseResFile(string fileName) { 104 auto f = File(fileName, "r"); 105 scope (exit) { 106 f.close(); 107 } 108 109 if (!f.isOpen()) 110 return false; 111 112 string _res_file_name = baseName(fileName, extension(fileName)); 113 string _loc = baseName(dirName(fileName)); 114 115 version(HUNT_DEBUG) trace("Reading lang resource file: ", fileName); 116 117 int line = 1; 118 while (!f.eof()) { 119 scope (exit) 120 line += 1; 121 string str = f.readln(); 122 str = strip(str); 123 if (str.length == 0) 124 continue; 125 if (str[0] == '#' || str[0] == ';') 126 continue; 127 auto len = str.length - 1; 128 129 auto site = str.indexOf("="); 130 if (site == -1) { 131 import std.format; 132 133 throw new Exception(format("the format is erro in file %s, in line %d : string: %s", 134 fileName, line, str)); 135 } 136 string key = str[0 .. site].strip; 137 if (key.length == 0) { 138 import std.format; 139 140 throw new Exception(format("the Key is empty in file %s, in line %d", 141 fileName, line)); 142 } 143 string value = str[site + 1 .. $].strip; 144 145 this._res[_loc][key] = value; // _res_file_name ~ "." ~ 146 } 147 return true; 148 } 149 150 // TODO: Tasks pending completion -@zhangxueping at 2020-06-05T18:15:42+08:00 151 // 152 // Add trans() 153 154 } 155 156 // private string _local /* = I18N_DEFAULT_LOCALE */ ; 157 158 // @property string getLocale() { 159 // if (_local) 160 // return _local; 161 // return I18n.instance().defaultLocale; 162 // } 163 164 // @property setLocale(string _l) { 165 // _local = toLower(_l); 166 // } 167 168 // string trans(A...)(string key, lazy A args) { 169 // import std.format; 170 // Appender!string buffer; 171 // string text = _trans(key); 172 // version(HUNT_DEBUG) tracef("format string: %s, key: %s, args.length: ", text, key, args.length); 173 // formattedWrite(buffer, text, args); 174 175 // return buffer.data; 176 // } 177 178 // deprecated("Using transWithLocale instead.") 179 // alias transfWithLocale = transWithLocale; 180 181 // string transWithLocale(A...)(string locale, string key, lazy A args) { 182 // import std.format; 183 // Appender!string buffer; 184 // string text = _transWithLocale(locale, key); 185 // formattedWrite(buffer, text, args); 186 187 // return buffer.data; 188 // } 189 190 // string transWithLocale(string locale, string key, JSONValue args) { 191 // import hunt.framework.util.Formatter; 192 // string text = _transWithLocale(locale, key); 193 // return StrFormat(text, args); 194 // } 195 196 // ///key is [filename.key] 197 // private string _trans(string key) { 198 // string defaultValue = key; 199 // I18n i18n = I18n.instance(); 200 // if (!i18n.isResLoaded) { 201 // logWarning("The lang resources haven't loaded yet!"); 202 // return key; 203 // } 204 205 // auto p = getLocale in i18n.resources; 206 // if (p !is null) { 207 // return p.get(key, defaultValue); 208 // } 209 // logWarning("unsupported local: ", getLocale, ", use default now: ", i18n.defaultLocale); 210 211 // p = i18n.defaultLocale in i18n.resources; 212 213 // if (p !is null) { 214 // return p.get(key, defaultValue); 215 // } 216 217 // logWarning("unsupported locale: ", i18n.defaultLocale); 218 219 // return defaultValue; 220 // } 221 222 // ///key is [filename.key] 223 // private string _transWithLocale(string locale, string key) { 224 // string defaultValue = key; 225 // I18n i18n = I18n.instance(); 226 // if (!i18n.isResLoaded) { 227 // logWarning("The lang resources has't loaded yet!"); 228 // return key; 229 // } 230 231 // auto p = locale in i18n.resources; 232 // if (p !is null) { 233 // return p.get(key, defaultValue); 234 // } 235 // logWarning("unsupported locale: ", locale, ", use default now: ", i18n.defaultLocale); 236 237 // p = i18n.defaultLocale in i18n.resources; 238 239 // if (p !is null) { 240 // return p.get(key, defaultValue); 241 // } 242 243 // logDebug("unsupported locale: ", i18n.defaultLocale); 244 245 // return defaultValue; 246 // } 247 248 // unittest{ 249 250 // I18n i18n = I18n.instance(); 251 // i18n.loadLangResources("./resources/translations"); 252 // i18n.defaultLocale = "en-us"; 253 // writeln(i18n.resources); 254 255 // /// 256 // setLocale("en-br"); 257 // assert( trans("message.hello-world") == "Hello, world"); 258 259 // /// 260 // setLocale("zh-cn"); 261 // assert( trans("email.subject") == "收件人"); 262 263 // setLocale("en-us"); 264 // assert( trans("email.subject") == "email.subject"); 265 266 // assert(trans("message.title") == "%s Demo"); 267 // assert(transf("message.title", "Hunt") == "Hunt Demo"); 268 269 // assert(transfWithLocale("zh-cn", "message.title", "Hunt") == "Hunt 示例"); 270 // } 271