L20n:Design:Gecko

From MozillaWiki
Jump to: navigation, search

Classess:

L20n

L20n is a singleton class that stores instances of L20nContexts and caches L20nResources and DocumentJSContexts.

L20nContext

L20nContext is a class that is generated per-document, collects resources into a single JS context and provides methods for getting string values for the given collection of resources.

Example:

foo.xul has three resources links:

* chrome://brand/content/brand
* chrome://browser/content/browser
* chrome://browser/content/menu

which are collected during document parsing and added to the asynchronous loader pool. When the parser encounters l10n-id attribute on any node it adds the node to a temporary queue and once all the resources are loaded L20nContext iterates through the nodes and expands the localizable values for the node.

Each L20nContext has a single javascript context that is cached in L20n singleton, so that when the same document is loaded twice it just takes the javascript context from cache.

Each L20nResource is cached in L20n singleton as well, so that once it's loaded it can be reused in many L20nContexts.

Example for ContentSink in a pseudo-language:

L20n:

L20n.resources = []
L20n.ctxs = []

L20n::GetContext(uri) {
  if (!L20n.ctxs[uri])
    L20n.ctxs[uri] = new L20nContext(uri);
  return L20n.ctxs[uri];
}

L20n::LoadResource(uri, l20nCtx) {
  if (L20n.resources[uri]) {
    l20nCtx.ctx.injectScript(L20n.resources[uri])
  } else {
    l20nCtx.loadPool.append(uri);
    var al = asyncLoader()
    al.onLoadComplete = function(data) {
      L20n.resources[uri] = data;
      l20nCtx.ctx.injectScript(L20n.resources[uri])
      l20nCtx.loadPool.remove(uri);
      if (l20nCtx.documentParsed && l20nCtx.loadPool.length == 0) {
        l20nCtx.LoadPendingNodes();
      }
    }
    al.open(uri);
  }
}

L20nContext:

L20nContext.ctx = JSContext();
L20nContext.loadPool = [];

L20nContext::AddReference (uri) {
  if (!L20n.resources[uri])
    L20n.LoadResource(uri, this);
}

L20nContext::LocalizePendingNodes() {
  foreach (node in this.loadPool) {
    var id = node.attrs["l10n-id"];
    this.LocalizeNode(id, node);
  }
}

L20nContext::LocalizeNode(id, node) {
  if (this.resourcesLoaded) {
    var entity = this.Get(id);
    foreach (entity.attrs as name)
      node.AddAttribute(name, entity.attrs[name]);
  } else {
    this.loadPool.append(node);
  }
}

L20nContext::Get(id) {
  return this.ctx.GetJSObject(id)
}

ContentSink:

ContentSink::HandlePI(pi) {
  if (pi.name == "localize")
    ctx = L20n.GetContext(mDocument->mURI);
    ctx.AddReference(pi.data);
}

ContentSink::EndOfDocument() {
  ctx = L20n.GetContext(mDocument->mURI);
  ctx.documentParsed = True;
  if (ctx.documentParsed && ctx.loadPool.length == 0) {
    ctx.LoadPendingNodes();
  }
  if (ctx.resourcesLoaded)
    ctx.LocalizePendingNodes()
}

ContentSink::AddAttribute(attr, node) {
  if (attr.name == "l10n-id") {
    ctx.LocalizeNode(node, attr.value)
  }
}