Mercurial > projects > mde
view mde/content/Translation.d @ 156:36df0ffe34d2
Fix to reload translations.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 18 Apr 2009 21:51:03 +0200 |
parents | 0520cc00c0cc |
children | 24d77c52243f |
line wrap: on
line source
/* LICENSE BLOCK Part of mde: a Modular D game-oriented Engine Copyright © 2007-2008 Diggory Hardy This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** Translation − internationalization module for translating strings * * Loads a set of names and optionally descriptions intended to provide a user- * friendly localised name to symbols defined in code or data files. * * Each locale may specify dependant locales which will be loaded and merged, * so that, for instance, variants of a language need not define common strings * in all variants. Dependencies are loaded in the order specified, including * all dependencies of first locale before any dependencies of other locales. * Circular dependencies are allowed. * * Entries may be extended to include a version number, intended to indicate * translations which may need updating to reflect a changed meaning. A * translation tool is needed to handle this really. */ module mde.lookup.Translation; import mde.file.paths; import mde.exception; import mde.file.mergetag.MTTagReader; import mde.file.deserialize; import tango.util.log.Log : Log, Logger; /** Loads all translation strings for current locale. * * See module description for details. * * Encoding used is UTF-8. */ struct Translation { /** Load the translation for the requested locale. * * Params: * name = The locale to load strings for. */ static { /** Get Translation instance for locale l10n, loading if not already * loaded. Only keeps one locale loaded. * * These are loaded from data/L10n/locale.mtt where locale is l10n and * files for dependant locales given in the header tag * <char[][]|depends=[...]>. * * On errors, a message is logged and the function continues, likely * resulting in some symbol names being untranslated. */ Translation get (char[] l10n) { if (loadedL10n != l10n) { loadedL10n = l10n; debug logger.trace ("Loading L10n: "~loadedL10n); transl.load (l10n); } return transl; } private Translation transl; private char[] loadedL10n; private Logger logger; static this () { logger = Log.lookup ("mde.lookup.Translation"); } } private void load (char[] l10n) { entries = null; // clear, then re-read char[][] files = [loadedL10n]; // initial locale plus dependencies size_t read = 0; // index in files of next file to read while (read < files.length) { try { MTTagReader reader = dataDir.makeMTTagReader ("L10n/"~files[read], PRIORITY.HIGH_LOW); bool isSecTag; while (reader.readTag (isSecTag)) { if (isSecTag) break; // end of header if (reader.tagID == "depends" && reader.tagType == "char[][]") { // append, making sure no duplicates are inserted char[][] deps = deserialize!(char[][]) (reader.tagData); f1: foreach (dep; deps) { foreach (f; files) if (f == dep) continue f1; files ~= dep; } } } char[] symPrefix; do { if (isSecTag) { if (reader.section.length == 0) symPrefix = ""; else symPrefix = reader.section ~ '.'; } else { if (reader.tagType == "entry") { char[] sym = symPrefix ~ reader.tagID; // If the tag already exists, don't replace it if (sym in entries) continue; try { Entry entry = deserialize!(Entry) (reader.tagData); if (entry.name is null) // This tag is invalid; ignore it throw new ContentException ("Tag has no name"); entries[sym] = entry; } catch (Exception e) { logger.error ("Exception reading L10n/{}.mtt:", files[read]); logger.error ("While parsing tag {}", sym); logger.error (e.msg); continue; } } } } while (reader.readTag (isSecTag)) } catch (Exception e) { logger.error ("Exception reading L10n/{}.mtt:", files[read]); logger.error (e.msg); } ++read; // continue to next file regardless of whether this file was read successfully } } /+ Getters for entries... not wanted now. alias entry opCall; /// Convenience alias /** Get the translation for the given identifier. * If no entry exists, the identifier will be returned. * * Optionally, the description can be returned. */ char[] entry (char[] id) { Entry* p = id in entries; if (p) { return p.name; } else { return id; } } /** ditto */ char[] entry (char[] id, out char[] description) { Entry* p = id in entries; if (p) { description = p.desc; return p.name; } else { return id; } } /** Alternative usage: return a Translation.Entry struct. */ Entry getStruct (char[] id) { Entry* p = id in entries; if (p) { return *p; } else { logger.warn ("Unable to find translation for: {}", id); Entry ret; ret.name = id; return ret; } } +/ /** This struct is used to store each translation name and description pair. * * Entries may also have a version field, but this is only needed for * writing/updating translations. */ struct Entry { char[] name; // The translated string char[] desc; // An optional description } Entry[char[]] entries; // all entries debug (mdeUnitTest) unittest { // Relies on files in unittest/data/L10n: locale-x.mtt for x in 1..4 // Struct tests Translation t = get ("locale-1"); Entry e = t.getStruct ("section-2.entry-1"); assert (e.name == "Test 1"); assert (e.desc == "Description"); e = t.getStruct ("section-2.entry-2"); assert (e.name == "Test 2"); assert (e.desc is null); // Dependency tests. Priority should be 1,2,3,4 (entries should come from first file in list that they appear in). char[] d = "1"; assert (t.entry ("section-1.file-1", d) == "locale-1"); assert (d is null); assert (t.entry ("section-1.file-2", d) == "locale-2"); assert (d == "desc2"); assert (t.entry ("section-1.file-3") == "locale-3"); assert (t.entry ("section-1.file-4") == "locale-4"); logger.info ("Unittest complete."); } }