summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/sdk/content/l10n-html.js
blob: f324623dc1b2755a54b5ebc456c0cf1146425608 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

module.metadata = {
  "stability": "unstable"
};

const { Ci, Cc, Cu } = require("chrome");
const core = require("../l10n/core");
const { loadSheet, removeSheet } = require("../stylesheet/utils");
const { process, frames } = require("../remote/child");
var observerService = Cc["@mozilla.org/observer-service;1"]
                      .getService(Ci.nsIObserverService);
const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");

const assetsURI = require('../self').data.url();

const hideSheetUri = "data:text/css,:root {visibility: hidden !important;}";

function translateElementAttributes(element) {
  // Translateable attributes
  const attrList = ['title', 'accesskey', 'alt', 'label', 'placeholder'];
  const ariaAttrMap = {
          'ariaLabel': 'aria-label',
          'ariaValueText': 'aria-valuetext',
          'ariaMozHint': 'aria-moz-hint'
        };
  const attrSeparator = '.';
  
  // Try to translate each of the attributes
  for (let attribute of attrList) {
    const data = core.get(element.dataset.l10nId + attrSeparator + attribute);
    if (data)
      element.setAttribute(attribute, data);
  }
  
  // Look for the aria attribute translations that match fxOS's aliases
  for (let attrAlias in ariaAttrMap) {
    const data = core.get(element.dataset.l10nId + attrSeparator + attrAlias);
    if (data)
      element.setAttribute(ariaAttrMap[attrAlias], data);
  }
}

// Taken from Gaia:
// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470
function translateElement(element) {
  element = element || document;

  // check all translatable children (= w/ a `data-l10n-id' attribute)
  var children = element.querySelectorAll('*[data-l10n-id]');
  var elementCount = children.length;
  for (var i = 0; i < elementCount; i++) {
    var child = children[i];

    // translate the child
    var key = child.dataset.l10nId;
    var data = core.get(key);
    if (data)
      child.textContent = data;

    translateElementAttributes(child);
  }
}
exports.translateElement = translateElement;

function onDocumentReady2Translate(event) {
  let document = event.target;
  document.removeEventListener("DOMContentLoaded", onDocumentReady2Translate,
                               false);

  translateElement(document);

  try {
    // Finally display document when we finished replacing all text content
    if (document.defaultView)
      removeSheet(document.defaultView, hideSheetUri, 'user');
  }
  catch(e) {
    console.exception(e);
  }
}

function onContentWindow(document) {
  // Accept only HTML documents
  if (!(document instanceof Ci.nsIDOMHTMLDocument))
    return;

  // Bug 769483: data:URI documents instanciated with nsIDOMParser
  // have a null `location` attribute at this time
  if (!document.location)
    return;

  // Accept only document from this addon
  if (document.location.href.indexOf(assetsURI) !== 0)
    return;

  try {
    // First hide content of the document in order to have content blinking
    // between untranslated and translated states
    loadSheet(document.defaultView, hideSheetUri, 'user');
  }
  catch(e) {
    console.exception(e);
  }
  // Wait for DOM tree to be built before applying localization
  document.addEventListener("DOMContentLoaded", onDocumentReady2Translate,
                            false);
}

// Listen to creation of content documents in order to translate them as soon
// as possible in their loading process
const ON_CONTENT = "document-element-inserted";
let enabled = false;
function enable() {
  if (enabled)
    return;
  addObserver(onContentWindow, ON_CONTENT, false);
  enabled = true;
}
process.port.on("sdk/l10n/html/enable", enable);

function disable() {
  if (!enabled)
    return;
  removeObserver(onContentWindow, ON_CONTENT);
  enabled = false;
}
process.port.on("sdk/l10n/html/disable", disable);