mozilla / qbrt

CLI to a Gecko desktop app runtime
Apache License 2.0
390 stars 31 forks source link

Proper way to add locale #134

Open Happy-Ferret opened 6 years ago

Happy-Ferret commented 6 years ago

I'm desperately trying to add locales to my code, using .properties files.

What is the proper way to do this?

So far I've been trying to manually load a chrome.manifest file in the root directory, however that fails.

Here's the code I put inside main.js for a PoC.

const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm } = Components;
const ChromeRegistry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci.nsIXULChromeRegistry);

const { console } = Cu.import('resource://gre/modules/Console.jsm', {});
const { Runtime } = Cu.import('resource://qbrt/modules/Runtime.jsm', {});
const { Services } = Cu.import('resource://gre/modules/Services.jsm', {});

Cu.import("resource://gre/modules/FileUtils.jsm");

var chrome = chromeToPath("chrome://app/content/chrome.manifest")
var file = new FileUtils.File(chrome);

Cm.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(file);
  ChromeRegistry.checkForNewChrome();

const BUNDLE_URI = "chrome://app/locale/app.properties";
const BUNDLE = Services.strings.createBundle(BUNDLE_URI);

function chromeToPath(aPath) {
    if (!aPath || !(/^chrome:/.test(aPath)))
        return; //not a chrome url
    var rv;

    var ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci["nsIIOService"]);
    var uri = ios.newURI(aPath, "UTF-8", null);
    var cr = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]);
    rv = cr.convertChromeURL(uri).spec;

    if (/^file:/.test(rv))
        rv = urlToPath(rv);
    else
        rv = urlToPath("file://" + rv);
    return rv;
}

function urlToPath(aPath) {
    if (!aPath || !/^file:/.test(aPath))
        return;
    var rv;
    var ph = Cc["@mozilla.org/network/protocol;1?name=file"]
        .createInstance(Ci.nsIFileProtocolHandler);
    rv = ph.getFileFromURLSpec(aPath).path;
    return rv;
}
Happy-Ferret commented 6 years ago

I also tried l20n (which seems to be where it's going anyways). No dice. Despite following the tutorial, I cannot seem to instantiate the translations. Neither with dist/web nor dist/gecko.

EDIT: Correction. l20n's dist/web build does indeed work here. I just had to load it as defer

i e <script defer src="node_modules/l20n/dist/web/l20n.js"></script>

Happy-Ferret commented 6 years ago

console.log(window.navigator.language returns nothing, thus my app always uses the <meta name="defaultLanguage" rather than the appropriate client language.

I've already tried inserting a few prefs but none of them seem to work.

      "intl.locale.matchOS": true,
      "intl.regional_prefs.use_os_locales": true,
      "services.sync.prefs.sync.intl.accept_languages": true,
      "intl.accept_languages": en-US, en",
      "general.useragent.locale": "en-US"
mykmelez commented 6 years ago

So far I've been trying to manually load a chrome.manifest file in the root directory, however that fails.

Hmm, how does it fail? Does it report an error, fail silently, or do something else?

var chrome = chromeToPath("chrome://app/content/chrome.manifest") var file = new FileUtils.File(chrome); Cm.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(file); ChromeRegistry.checkForNewChrome(); const BUNDLE_URI = "chrome://app/locale/app.properties";

Hmm, as I understand it, this should work, if your chrome.manifest file is in the top-level directory of your app, and it contains an instruction like locale app en-US path/to/locale/, and path/to/locale/ is relative to the top-level directory of your app. If you can point me to your source, I can debug further.

Nevertheless, you're right that l20n is the future of localization for Firefox, so it's probably a better solution for your use case as well.

mykmelez commented 6 years ago

console.log(window.navigator.language returns nothing, thus my app always uses the <meta name="defaultLanguage" rather than the appropriate client language.

I've already tried inserting a few prefs but none of them seem to work.

It looks like intl.accept_languages is a "complex value" pref whose value isn't the literal string to which it's set but rather comes from a file that is referenced by the pref:

http://searchfox.org/mozilla-central/source/modules/libpref/init/all.js#2312

After a bit of searching the codebase, I found some code that does something like this:

var setLocalizedPref = function (pref, value) {
  let pls = Cc["@mozilla.org/pref-localizedstring;1"]
            .createInstance(Ci.nsIPrefLocalizedString);
  pls.data = value;
  Services.prefs.setComplexValue(pref, Ci.nsIPrefLocalizedString, pls);
}
this.setLocalizedPref("intl.accept_languages", "de-de");
console.log(window.navigator.language);

Adding that to the end of examples/hello-world-html/main.js causes this line to be output on the console:

console.log: de-DE

Happy-Ferret commented 6 years ago

It blocks the UI and displays the following error message

/qbrt/dist/win32/runtime/omni.ja!/components/nsPrompter.js, line 350: NS_ERROR_NOT_AVAILABLE: Cannot call openModalWindow on a hidden window

And thanks. The other code works. Now to figure out how to actually make use of that. Ideally, that value should always reflect the system locale.