Localization (and Enum Resolve) Example

When I was first trying to localize my extension I couldn’t find any localized extensions to mimic, so it took a little trial and error to figure out how to connect the pieces (mostly json was being finicky). I thought I’d post a link to my extension under this subject in case it helps anyone else who is just starting out:
Translate In Place | Nova Extensions

In addition to being a localization example, I used Translate in Place to localize Translate in Place so if you’re thinking about localizing your extension then it can help with that initial translation (until someone fluent in the language can hopefully submit any corrections).

A couple of things I’m finding useful:

1.) Instead of using the English version of the text as the key:
nova.localize(“The quick brown fox jumped”);
I use a descriptive index string instead:
nova.localize(“_sample_text”);
and include a translation file for English as well (en.lproj/strings.json). The extension code contains no user-facing strings, only localization keys.

I find this is:

  • much easier to read in the code,
  • you only ever have to change each language in one place (not change the English version in every translation file as well), and
  • less likely to mis-match the keys.

Note that if someone is using a language you haven’t created a localization for, Nova will default to English (it won’t just show them the localization index key). (*Edit:* See thread below – only if English is in the user’s list of alternate languages)

2.) I start all my localization keys with _ to make it easier to see this is an index key, not the expected output text (makes them easier to spot in the interface when the translation isn’t mapped correctly). AND it helps avoid conflicts / accidental-translations with point 3.

3.) Translate in Place has a handy localization function _l() that takes a variable number of parameters in addition to the initial key so that it’s easy to build compound localized strings.

e.g., If we have these strings defined:
"_err.general": “This is a general error of type ‘%@1’ because you entered ‘%@2’ into the dialog.”
"_err_type.syntax": “Syntax Error”
and one makes the call:
_l(‘_err.general’, ‘_err_type.syntax’, user_entered_text );
the resulting string would be:
“This is a general error of type ‘Syntax Error’ because you entered ‘foobar’ into the dialog.”

Please steal the function if it would be useful to you. :slight_smile:

This extension also makes use of the enum-resolve callback to build the language menus since the alphabetical order for languages changes for each language (e.g., English, Anglais, Ingles), so if you’re wondering what using an enum-resolve callback looks like, you can check it out as an example for that as well.

Hope it helps!

2 Likes

Note that if someone is using a language you haven’t created a localization for, Nova will default to English (it won’t just show them the localization index key).

Unless something undocumented changed in recent Nova versions, this is, alas, not the case. I have used much the same mechanism as you to localise my JXA and µESLint extension, and as it turned out, Nova does not fall back to English. Instead, all languages in the macOS system preferences are tried, in order; if none matches the available localisations, raw placeholder strings will be displayed.

Ah. Darn. Thanks for the correction.

For testing, I switched my system to use German as the primary language (not localized yet), relaunched Nova, and the extension defaulted to using the English strings so I assumed it defaulted to English.

But by the sounds of it, it was only because English was still one of the options in the “Languages & Region” pane in System Preferences that it found it.

So if I want to keep using shorter ‘index keys’ instead of full-text strings, I should include as many .lproj language files as is practical, even if they all use a copy of the en.lproj strings (until I get around to localizing them, too).

A bit kludgy but shorter keys are just much more maintainable.

So if I want to keep using shorter ‘index keys’ instead of full-text strings, I should include as many .lproj language files as is practical, even if they all use a copy of the en.lproj strings (until I get around to localizing them, too).

A bit kludgy but shorter keys are just much more maintainable.

Another solution is something I have been trying for size: check available languages, compare to a available localisations and create a dummy localisation with copies of the English one for the user’s main language on extension startup. This is admittedly hacky, but should work for any locale, especially if you add a fallback notifying the user of the problem and workaround (add English to the language pane) in case of error.

Ha! Nice. Just-in-time localization.