diff --git a/doc/json-config-parameters.md b/doc/json-config-parameters.md
index 48014a8..f49d920 100644
--- a/doc/json-config-parameters.md
+++ b/doc/json-config-parameters.md
@@ -23,6 +23,12 @@ If set, the value is displayed as the panorama's author. If no author is
desired, don't set this parameter.
+### `strings` (dictionary)
+
+Allows user-facing strings to be changed / translated.
+See `defaultConfig.strings` definition in `pannellum.js` for more details.
+
+
### `basePath` (string)
This specifies a base path to load the images from.
@@ -165,11 +171,6 @@ Specifies the title to be displayed while the load button is displayed.
Specifies the author to be displayed while the load button is displayed.
-### `loadButtonLabel` (string)
-
-Label to display on load button. Defaults to `Click to Load Panorama`.
-
-
### `horizonPitch` and `horizonRoll` (number)
Specifies pitch / roll of image horizon, in degrees (for correcting
diff --git a/src/js/pannellum.js b/src/js/pannellum.js
index 4ef03b0..46d30b0 100644
--- a/src/js/pannellum.js
+++ b/src/js/pannellum.js
@@ -100,10 +100,33 @@ var defaultConfig = {
hotSpotDebug: false,
backgroundColor: [0, 0, 0],
animationTimingFunction: timingFunction,
- loadButtonLabel: 'Click to\nLoad\nPanorama',
draggable: true,
};
+// Translatable / configurable strings
+// Some strings contain '%s', which is a placeholder for inserted values
+// When setting strings in external configuration, `\n` should be used instead of `
` to insert line breaks
+defaultConfig.strings = {
+ // Labels
+ loadButtonLabel: 'Click to
Load
Panorama',
+ loadingLabel: 'Loading...',
+ bylineLabel: 'by %s', // One substitution: author
+
+ // Errors
+ noPanoramaError: 'No panorama image was specified.',
+ fileAccessError: 'The file %s could not be accessed.', // One substitution: file URL
+ malformedURLError: 'There is something wrong with the panorama URL.',
+ iOS8WebGLError: "Due to iOS 8's broken WebGL implementation, only " +
+ "progressive encoded JPEGs work for your device (this " +
+ "panorama uses standard encoding).",
+ genericWebGLError: 'Your browser does not have the necessary WebGL support to display this panorama.',
+ textureSizeError: 'This panorama is too big for your device! It\'s ' +
+ '%spx wide, but your device only supports images up to ' +
+ '%spx wide. Try another device.' +
+ ' (If you\'re the author, try scaling down the image.)', // Two substitutions: image width, max image width
+ unknownError: 'Unknown error. Check developer console.',
+}
+
var usedKeyNumbers = [16, 17, 27, 37, 38, 39, 40, 61, 65, 68, 83, 87, 107, 109, 173, 187, 189];
// Initialize container
@@ -154,7 +177,6 @@ uiContainer.appendChild(infoDisplay.container);
infoDisplay.load = {};
infoDisplay.load.box = document.createElement('div');
infoDisplay.load.box.className = 'pnlm-load-box';
-infoDisplay.load.box.innerHTML = '
Loading...
'; infoDisplay.load.lbox = document.createElement('div'); infoDisplay.load.lbox.className = 'pnlm-lbox'; infoDisplay.load.lbox.innerHTML = ''; @@ -303,7 +325,7 @@ function init() { panoImage = config.panorama; } else { if (config.panorama === undefined) { - anError('No panorama image was specified.'); + anError(config.strings.noPanoramaError); return; } panoImage = new Image(); @@ -326,7 +348,7 @@ function init() { var a = document.createElement('a'); a.href = e.target.src; a.innerHTML = a.href; - anError('The file ' + a.outerHTML + ' could not be accessed.'); + anError(config.strings.fileAccessError.replace('%s', a.outerHTML)); }; for (i = 0; i < panoImage.length; i++) { @@ -362,7 +384,7 @@ function init() { var a = document.createElement('a'); a.href = encodeURI(p); a.innerHTML = a.href; - anError('The file ' + a.outerHTML + ' could not be accessed.'); + anError(config.strings.fileAccessError.replace('%s', a.outerHTML)); } var img = this.response; parseGPanoXMP(img); @@ -398,7 +420,7 @@ function init() { xhr.open('GET', p, true); } catch (e) { // Malformed URL - anError('There is something wrong with the panorama URL.'); + anError(config.strings.malformedURLError); } xhr.responseType = 'blob'; xhr.setRequestHeader('Accept', 'image/*,*/*;q=0.9'); @@ -484,11 +506,8 @@ function parseGPanoXMP(image) { // with non-progressive encoded JPEGs. if (navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad).* os 8_/)) { var flagIndex = img.indexOf('\xff\xc2'); - if (flagIndex < 0 || flagIndex > 65536) { - anError("Due to iOS 8's broken WebGL implementation, only " + - "progressive encoded JPEGs work for your device (this " + - "panorama uses standard encoding)."); - } + if (flagIndex < 0 || flagIndex > 65536) + anError(config.strings.iOS8WebGLError); } var start = img.indexOf('' + config.strings.loadButtonLabel + '
'; + infoDisplay.load.box.innerHTML = '' + config.strings.loadingLabel + '
'; + // Process other options for (var key in config) { if (config.hasOwnProperty(key)) { @@ -1872,7 +1916,7 @@ function processOptions(isPreview) { break; case 'author': - infoDisplay.author.innerHTML = 'by ' + escapeHTML(config[key]); + infoDisplay.author.innerHTML = config.strings.bylineLabel.replace('%s', escapeHTML(config[key])); infoDisplay.container.style.display = 'inline'; break; @@ -1940,10 +1984,6 @@ function processOptions(isPreview) { startOrientation(); } break; - - case 'loadButtonLabel': - controls.load.innerHTML = '' + escapeHTML(config[key]) + '
'; - break; } } }