mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Implemented UI l10n file loading.
Uses a polyfill for the Encoding API. Start of #217.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,210 @@
|
||||
/* The text-encoding module is only strictly required for Node.js, and may not
|
||||
be required in recent browsers, so don't specify it for them. */
|
||||
'use strict';
|
||||
(function(root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define([], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node. Does not work with strict CommonJS, but
|
||||
// only CommonJS-like environments that support module.exports,
|
||||
// like Node.
|
||||
module.exports = factory(require('text-encoding'));
|
||||
} else {
|
||||
// Browser globals
|
||||
root.jedGettextParser = factory();
|
||||
}
|
||||
}(this, function(textEncoding) {
|
||||
/* Return what this module exports. */
|
||||
|
||||
if (textEncoding) {
|
||||
var TextDecoder = textEncoding.TextDecoder;
|
||||
} else {
|
||||
var TextDecoder = window.TextDecoder;
|
||||
}
|
||||
|
||||
function Parser() {
|
||||
this._littleEndian;
|
||||
this._dataView;
|
||||
this._encoding;
|
||||
|
||||
this._originalOffset;
|
||||
this._translationOffset;
|
||||
}
|
||||
Parser.prototype._MAGIC = 0x950412de;
|
||||
|
||||
Parser.prototype._getEndianness = function() {
|
||||
/* MO files can be big or little endian, independent of the source or current platform. Use DataView's optional get*** argument to set the endianness if necessary. */
|
||||
if (this._dataView.getUint32(0, true) == this._MAGIC) {
|
||||
this._littleEndian = true;
|
||||
} else if (this._dataView.getUint32(0, false) == this._MAGIC){
|
||||
this._littleEndian = false;
|
||||
} else {
|
||||
throw new Error('Not a gettext binary message catalog file.');
|
||||
}
|
||||
}
|
||||
|
||||
Parser.prototype._readTranslationPair = function(originalOffset, translationOffset) {
|
||||
var length, position, idBytes, strBytes;
|
||||
/* Get original byte array, that forms the key. */
|
||||
length = this._dataView.getUint32(originalOffset, this._littleEndian);
|
||||
position = this._dataView.getUint32(originalOffset + 4, this._littleEndian);
|
||||
try {
|
||||
idBytes = new Uint8Array(this._dataView.buffer, position, length);
|
||||
} catch(e) {
|
||||
throw new Error('The given ArrayBuffer data is corrupt or incomplete.');
|
||||
}
|
||||
|
||||
/* Get translation byte array, that forms the value. */
|
||||
length = this._dataView.getUint32(translationOffset, this._littleEndian);
|
||||
position = this._dataView.getUint32(translationOffset + 4, this._littleEndian);
|
||||
try {
|
||||
strBytes = new Uint8Array(this._dataView.buffer, position, length);
|
||||
} catch(e) {
|
||||
throw new Error('The given ArrayBuffer data is corrupt or incomplete.');
|
||||
}
|
||||
|
||||
return {
|
||||
id: idBytes,
|
||||
str: strBytes
|
||||
};
|
||||
}
|
||||
|
||||
Parser.prototype._parseHeader = function() {
|
||||
/* Read translation header. This is stored as a msgstr where the msgid
|
||||
is '', so it's the first entry in the translation block, since
|
||||
strings are sorted. Assume that the header is in UTF-8. We only want
|
||||
the language, encoding and plural forms values, which should all be
|
||||
ASCII anyway.
|
||||
*/
|
||||
var msgBytes = this._readTranslationPair(this._originalOffset, this._translationOffset);
|
||||
|
||||
var language, pluralForms;
|
||||
if (msgBytes.id.byteLength == 0) {
|
||||
var decoder = new TextDecoder();
|
||||
var str = decoder.decode(msgBytes.str);
|
||||
|
||||
var headers = {};
|
||||
str.split("\n").forEach(function(line){
|
||||
/* Header format is like HTTP headers. */
|
||||
var parts = line.split(':');
|
||||
var key = parts.shift().trim();
|
||||
var value = parts.join(':').trim();
|
||||
headers[key] = value;
|
||||
});
|
||||
|
||||
/* Get encoding if not given. */
|
||||
if (!this._encoding) {
|
||||
var pos = headers['Content-Type'].indexOf('charset=');
|
||||
|
||||
if (pos != -1 && pos + 8 < headers['Content-Type'].length) {
|
||||
/* TextDecoder expects a lowercased encoding name. */
|
||||
this._encoding = headers['Content-Type'].substring(pos + 8).toLowerCase();
|
||||
}
|
||||
if (!this._encoding) {
|
||||
this._encoding = 'utf-8';
|
||||
}
|
||||
}
|
||||
|
||||
/* Get language from header. */
|
||||
language = headers['Language'];
|
||||
|
||||
/* Get plural forms from header. */
|
||||
pluralForms = headers['Plural-Forms'];
|
||||
}
|
||||
|
||||
return {
|
||||
'': {
|
||||
'domain': '',
|
||||
'lang': language,
|
||||
'plural_forms': pluralForms,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Parser.prototype._splitPlurals = function(msgid, msgstr) {
|
||||
/* Need to handle plurals. Don't need to handle contexts, because Jed
|
||||
expects the context-msgid strings to be its keys. However, plural
|
||||
translations must be split into an array of strings. Jed only wants
|
||||
the first part of a plural as its key. */
|
||||
return {
|
||||
id: msgid.split('\u0000')[0],
|
||||
str: msgstr.split('\u0000')
|
||||
}
|
||||
}
|
||||
|
||||
Parser.prototype.parse = function(buffer, encoding) {
|
||||
|
||||
/* A mo file can be empty apart from its magic, revision, strings count,
|
||||
offsets and hash table size, but fields for these must all exist, so
|
||||
verify the file is large enough. */
|
||||
if (buffer.byteLength < 28) {
|
||||
throw new Error('The given ArrayBuffer is too small to hold a valid .mo file.');
|
||||
}
|
||||
|
||||
this._dataView = new DataView(buffer);
|
||||
this._encoding = encoding;
|
||||
|
||||
this._getEndianness();
|
||||
|
||||
/* Get size and offsets. Skip the revision and hash table, they're
|
||||
unnecessary. */
|
||||
var stringsCount = this._dataView.getUint32(8, this._littleEndian);
|
||||
this._originalOffset = this._dataView.getUint32(12, this._littleEndian);
|
||||
this._translationOffset = this._dataView.getUint32(16, this._littleEndian);
|
||||
|
||||
/* Parse header for info, and use it to create the Jed locale_data
|
||||
object 'header'. */
|
||||
var jedLocaleData = this._parseHeader();
|
||||
|
||||
/* Create a TextDecoder for encoding conversion. */
|
||||
try {
|
||||
var decoder = new TextDecoder(this._encoding);
|
||||
} catch(e) {
|
||||
throw new Error("The encoding label provided ('" + this._encoding + "') is invalid.");
|
||||
}
|
||||
|
||||
/* Now get translations. */
|
||||
var originalOffset = this._originalOffset + 8;
|
||||
var translationOffset = this._translationOffset + 8;
|
||||
for (var i = 1; i < stringsCount; ++i) {
|
||||
var msgBytes = this._readTranslationPair(originalOffset, translationOffset);
|
||||
var msg = this._splitPlurals( decoder.decode(msgBytes.id), decoder.decode(msgBytes.str) );
|
||||
|
||||
jedLocaleData[msg.id] = [ null ].concat(msg.str);
|
||||
|
||||
originalOffset += 8;
|
||||
translationOffset += 8;
|
||||
}
|
||||
|
||||
return jedLocaleData;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
mo: {
|
||||
parse: function(buffer, options) {
|
||||
|
||||
/* Leave the encoding undefined if no options are given. */
|
||||
options = options || { domain: 'messages' };
|
||||
options.domain = options.domain || 'messages';
|
||||
|
||||
if (buffer && buffer.byteLength == 0) {
|
||||
throw new Error('Given ArrayBuffer is empty.');
|
||||
}
|
||||
|
||||
if (!buffer || Object.prototype.toString.call(buffer) != '[object ArrayBuffer]') {
|
||||
throw new Error('First argument must be an ArrayBuffer.');
|
||||
}
|
||||
|
||||
var parser = new Parser();
|
||||
var messages = parser.parse(buffer, options.encoding);
|
||||
|
||||
messages[''].domain = options.domain;
|
||||
var locale_data = {};
|
||||
locale_data[options.domain] = messages;
|
||||
return locale_data;
|
||||
}
|
||||
}
|
||||
};
|
||||
}));
|
||||
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['jed', 'jedGettextParser'], factory);
|
||||
} else {
|
||||
// Browser globals
|
||||
root.amdWeb = factory(root.jed, root.jedGettextParser);
|
||||
}
|
||||
}(this, function (jed, jedGettextParser) {
|
||||
//use b in some fashion.
|
||||
|
||||
// Just return a value to define the module export.
|
||||
// This example returns an object, but the module
|
||||
// can return a function as the exported value.
|
||||
return {
|
||||
|
||||
loadLocaleData: function(locale) {
|
||||
|
||||
var url = 'loot://l10n/' + locale + '/LC_MESSAGES/loot.mo';
|
||||
|
||||
return new Promise(function(resolve, reject){
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.addEventListener('readystatechange', function(evt){
|
||||
if (evt.target.readyState == 4) {
|
||||
/* Status is 0 for local file URL loading. */
|
||||
if (evt.target.status >= 200 && evt.target.status < 400) {
|
||||
resolve(jedGettextParser.mo.parse(evt.target.response));
|
||||
} else {
|
||||
reject(new Error('XHR Error'));
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
xhr.send();
|
||||
});
|
||||
},
|
||||
|
||||
applyLocale: function(locale) {
|
||||
this.loadLocaleData(locale).then(function(result){
|
||||
console.log(result);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
}));
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
var marked;
|
||||
var l10n;
|
||||
var loot = {
|
||||
hasFocus: true,
|
||||
installedGames: [],
|
||||
@@ -1341,6 +1342,7 @@ function initVars() {
|
||||
try {
|
||||
loot.settings = JSON.parse(results[2]);
|
||||
applySavedFilters();
|
||||
l10n.applyLocale(loot.settings.language);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.log('getSettings response: ' + results[2]);
|
||||
@@ -1441,8 +1443,9 @@ function onFocus(evt) {
|
||||
require.config({
|
||||
baseUrl: "js",
|
||||
});
|
||||
require(['marked', 'order!custom', 'order!plugin'], function(response) {
|
||||
marked = response;
|
||||
require(['marked', 'l10n', 'order!custom', 'order!plugin'], function(markedResponse, l10nResponse) {
|
||||
marked = markedResponse;
|
||||
l10n = l10nResponse;
|
||||
/* Make sure settings are what I want. */
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
|
||||
@@ -564,5 +564,6 @@ along with LOOT. If not, see <http://www.gnu.org/licenses/>.
|
||||
<p>See LOOT's documentation, accessed through the <q>Help</q> menu, for further details.</p>
|
||||
<button class="accept">OK</button>
|
||||
</dialog>
|
||||
<script src="js/encoding.js"></script>
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
Reference in New Issue
Block a user