Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Localize add-on #5

Closed
wants to merge 9 commits into from
Closed

Localize add-on #5

wants to merge 9 commits into from

Conversation

rugk
Copy link
Owner

@rugk rugk commented Nov 28, 2020

This first localizes the options page. I had one problem with a non-breaking space, but I could fix that by myself.

I suggest we review any change before pushing to the main branch, @tdulcet. Later I may also protect the main branch before we do any release to enforce this. (For now for small fixes that really need no review pushing to the main branch is okay, IMHO.)

I guess you are a native speaker of English, so feel free to review the English parts.
(I doubt you can review the German parts, so you can ignore them, if you cannot. :) )

TODO:

  • We also need to localize the context menu. In the UnicodeFontHandler you seem to create the titles dynamically. This is of course clever and okay in general, however, obviously needs to be changed now. Also pay attention to how the keys in the messages.json are currently named – you can build that dynamically, but yeah.
    What we'd need to decide is whether we write the camelCaseStyle e.g. in the translation file directly or not. At least for the casing stuff, I’d tend to write it in there, so translators could choose a different highlighting or so if that is required.
    (Also, while you are at it, expand the aid variable, it is way too short and not self-explaining.)

* add English translation as template
* add German translation
@rugk rugk added this to the next milestone Nov 28, 2020
@rugk rugk requested a review from tdulcet November 28, 2020 20:27
@rugk rugk added code quality Improving the code for the code's sake… i18n/l10n internationalisation & localisation and removed code quality Improving the code for the code's sake… labels Nov 28, 2020
@tdulcet
Copy link
Collaborator

tdulcet commented Nov 29, 2020

I suggest we review any change before pushing to the main branch

That is fine with me.

We also need to localize the context menu

The strings in these two arrays are what need to be localized:

/**
* Case IDs
*
* @public
* @const
* @type {Object.<string>}
*/
export const caseIds = Object.freeze(["Lowercase", "Uppercase", "Capitalize Each Word", "Toggle Case"]);
/**
* Font IDs
*
* @public
* @const
* @type {Object.<string>}
*/
export const fontIds = Object.freeze(["Superscript", "Small Caps", "All Small Caps", "Unicase", "separator", "Serif bold", "Serif italic", "Serif bold italic", "Sans-serif", "Sans-serif bold", "Sans-serif italic", "Sans-serif bold italic", "Script", "Script bold", "Fraktur", "Fraktur bold", "Monospace", "Double-struck", "separator", "Circled", "Circled (black)", "Squared", "Squared (black)", "Fullwidth"]);
Note that since the strings in the second array are converted into the respective Unicode font, they must contain only ASCII characters, so probably will not be translatable into many languages.

What we'd need to decide is whether we write the camelCaseStyle e.g. in the translation file directly or not.

It would be easy to dynamically convert those above IDs into Camel case and then use the i18n.getMessage() API to get the localized string.

Also, while you are at it, expand the aid variable, it is way too short and not self-explaining.

The variable is just those above IDs dynamically converted into Kebab case to use as an index into the changeCase (see here) and font (see here) objects respectively.

Only the quotes replacement is done immediately.
</span><br>
<span class="helper-text" data-i18n="__MSG_optionHelperTextAutocorrectionEmoji__" data-opt-i18n-keep-children>
For Emoji autocorrection, including <code data-i18n="__MSG_optionHelperTextAutocorrectionEmojiColonsCode__">:colon:</code> shortcodes and Emoticons, please also try out our <a href="https://addons.mozilla.org/firefox/addon/awesome-emoji-picker/" data-i18n="__MSG_optionHelperTextAutocorrectionEmojiLinkText__" data-i18n-href="__MSG_optionHelperTextAutocorrectionEmojiLink__">🤩&nbsp;Awesome Emoji Picker</a> add-on.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it does not effect this PR, note that the AMO URL will need to be updated after I create the Thunderbird and Chrome PRs for the Awesome Emoji Picker...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah indeed, at best dynamically. Yeah, thanks for the notice.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem.

@rugk
Copy link
Owner Author

rugk commented Nov 29, 2020

It would be easy to dynamically convert those above IDs into Camel case and then use the i18n.getMessage() API to get the localized string.

That’s the way to go IMHO then.

The variable is just those above IDs dynamically converted into Kebab case to use as an index into the changeCase (see here) and font (see here) objects respectively

Yeah, we cannot/should not o t6hsi with the translated strings of course. Never use translated strings as IDs… Your hint about nin-Unicode chars in many languages is indeed an important thing to keep in mind, I did not though about this.

@rugk
Copy link
Owner Author

rugk commented Nov 30, 2020

It would be easy to dynamically convert those above IDs into Camel case

Just looked into it and I doubt it's so trivial. The problem is the Ids in Fonts.js are no IDs, but English strings – some have spaces, some have hyphens etc. with different casing.
IMHO the better way would be: Fonts.jscontains the ID only – it should just folow some srict format, . Each ID may then be easily converted to camelCase or the IDs already are camelCase and we return the translation. I.e. never hardcode translations in the JS files.
fontIds should be the same as fonts. Also for fontIds you can then just do an Object.keys(fonts).

Also, regarding i18n, we should never apply any effects on the localized strings. This should be done to allow translators to translate "superscript" e.g. in Russian (translated with DeepL) to this:

надстрочный индекс (ˢᵘᵖᵉʳˢᶜʳⁱᵖᵗ)

So they can still give an example on how it looks while using their own character set to properly translate the thing.
Do you @tdulcet want to change this?

BTW this line does nothing, at most it logs something to the console.

@tdulcet
Copy link
Collaborator

tdulcet commented Dec 1, 2020

Never use translated strings as IDs…

Yes, of course. The IDs would stay the same. The translated strings would only be used for the title that is displayed to the user.

Just looked into it and I doubt it's so trivial. The problem is the Ids in Fonts.js are no IDs, but English strings – some have spaces, some have hyphens etc. with different casing.

Yeah, I only wanted to have one copy of those strings to use for both the IDs and titles, to make this easier to maintain. I was thinking we could use some simple code like this to convert the strings into Camel case:

function camelCase(atext) {
	const [head, ...tail] = atext;
	return head.toLowerCase() + tail.join('').split(" ").map(([h, ...t]) => h.toUpperCase() + t.join('')).join('');
}
// ...
const title = browser.i18n.getMessage(camelCase(id.toLowerCase())) || id;

You could of course use some more complex code with regular expressions like this to also remove the hyphens and parentheses:

function camelCase(atext) {
	const [head, ...tail] = atext;
	return head.toLowerCase() + tail.join('').replace(/(?<=\P{Alpha})\p{Alpha}\S*/gu, ([h, ...t]) => h.toUpperCase() + t.join('')).replace(/\P{Alpha}+/gu, '');
}

Also for fontIds you can then just do an Object.keys(fonts).

Some of the strings are "separator" to indicate that there should be a separator there, so Object.keys() would not work.

Also, regarding i18n, we should never apply any effects on the localized strings.

OK, I thought it would be nice for the users to be able see the effect of the options in their language without the translators having to manually convert anything. For example, if the Russian translator used надстрочный индекс (Superscript), it would automatically convert to надстрочный индекс (ˢᵘᵖᵉʳˢᶜʳⁱᵖᵗ).

BTW this line does nothing, at most it logs something to the console.

No, it converts the Font strings into an array symbols using Array.from() for use by the changeFont() function. Array.from() is a new ES 6 feature to properly split Unicode strings (see here and here). Try logging the fonts object before and after that line to see the difference.

Let me know if you need me to make any changes.

@rugk
Copy link
Owner Author

rugk commented Dec 1, 2020

thinking we could use some simple code like this to convert the strings into Camel case:

Sorry but "simple"? The code you presented there is really long and one really needs to think about it to get what it does… 🙃

Some of the strings are "separator" to indicate that there should be a separator there, so Object.keys() would not work.

Well object.keys and then just filter.

No, it converts the Font strings into an array symbols using Array.from() for use by the changeFont() function. Array.from() is a new ES 6 feature to properly split Unicode strings (see here and here). Try logging the fonts object before and after that line to see the difference.

I did not test, but actually should not make a difference. Array.from "creates a new, shallow-copied Array instance from an array-like or iterable object", i.e. it returns the new array. It does not modify the existing one. So you need to assign the result somewhere…

@rugk
Copy link
Owner Author

rugk commented Dec 1, 2020

Ah sorry, indeed it assigns thing in fonts[key] = , sorry I have missed that. Well, could need some explanatory comment or put it into a function or so.

@tdulcet
Copy link
Collaborator

tdulcet commented Dec 2, 2020

Sorry but "simple"? The code you presented there is really long and one really needs to think about it to get what it does… 🙃

Oh, sorry. I edited my post and changed the code to make it more general after I wrote that. Here is the original simpler version, which assumes the string is lowercase.

function camelCase(atext) {
	const [head, ...tail] = atext.split(" ");
	return head + tail.map(([h, ...t]) => h.toUpperCase() + t.join('')).join('');
}

Well object.keys and then just filter.

Some of the strings in the fontIDs array are "separator" to indicate that there should be a separator (see here). Using Object.keys() on the fonts object would eliminate all the separators from the menu. It also would be a lot more difficult to then dynamically generate the titles from the Kebab case IDs.

@rugk
Copy link
Owner Author

rugk commented Dec 2, 2020

Well… in any case it needs to be changed, because…

Yeah, I only wanted to have one copy of those strings to use for both the IDs and titles, to make this easier to maintain.

This assumption/aim is not there anymore if we localize. We should kjeep all strings in the messages.json and the code should only contain IDs of some sort.

@rugk rugk self-assigned this Dec 2, 2020
@tdulcet tdulcet self-requested a review December 2, 2020 17:19
function capitalizeEachWord(text) {
// Regular expression Unicode property escapes and lookbehind assertions require Firefox/Thunderbird 78
// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#bcd:javascript.builtins.RegExp
// return text.replace(/(?<=^|\P{Alpha})\p{Alpha}\S*/gu, ([h, ...t]) => h.toLocaleUpperCase() + t.join(''));
Copy link
Collaborator

@tdulcet tdulcet Dec 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this line commented out? Edit: Looks like you were trying to resolve the Parsing error: Invalid regular expression with ESLint. Try updating the 2017 on this line: https://github.com/rugk/unicodify/blob/main/.eslintrc#L4 to at least 2019 or just 9 (see here).

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah need to comment in again.

// convert string back to Symbol
const caseId = caseByString[info.menuItemId];
const fontId = fontByString[info.menuItemId];
if (caseId) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe if (info.menuItemId in caseByString) { to eliminate the temporary variables. Same below.

menuItemsToShow = contextMenuList.filter((id) => !fontValues.includes(id) );
}
menuItemsToShow
.filter((id, index) => (index !== 0 || id !== SEPARATOR))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not work if the user only enables the change case options, as they get a bunch of separators at the bottom:
image

} else {
menus.create({
"id": id.toString(),
"title": browser.i18n.getMessage(menuTranslation[id]),
Copy link
Collaborator

@tdulcet tdulcet Dec 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line prevents the user from seeing the effect of the options. It will also fail if any of the strings have not been translated, as they currently have not for German.

Copy link
Owner Author

@rugk rugk Dec 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I do plan to move the effect of the translations into the translation itself. As said, to make things like надстрочный индекс (ˢᵘᵖᵉʳˢᶜʳⁱᵖᵗ) possible and allow translators to customize the appearance, if they needs to.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, as I said it would be possible either way since the changeFont() function ignores non-ASCII characters...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay okay, we'll try it like this.

@@ -5,146 +5,135 @@ import * as BrowserCommunication from "/common/modules/BrowserCommunication/Brow
import { isMobile } from "./MobileHelper.js";

import { COMMUNICATION_MESSAGE_TYPE } from "/common/modules/data/BrowserCommunicationTypes.js";
import { caseIds, fontIds, fonts } from "/common/modules/data/Fonts.js";
import { CASE, FONT, SEPARATOR, contextMenuList, menuTranslation, fontMap, caseByString, fontByString } from "/common/modules/data/Fonts.js";
Copy link
Collaborator

@tdulcet tdulcet Dec 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this will include at least four duplicate copies of the strings, which will both increase memory use and make this feature more difficult to maintain.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it exaggerated a little yesterday as I "only" wanted to use Symbols as IDs, but then stumbled upon all these errors that made it harder than it should be. E.g. you can only use strings in the contextMenu API as an ID – respectively if you use a Symbol it does not match the Symbol that was passed to it, before.
Still need to think whether there may be a better solution.

"fullwidth": "!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
* @type {Object.<FONT, string>}
*/
const fonts = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This object should be frozen now.

@rugk
Copy link
Owner Author

rugk commented Feb 23, 2021

Some status update, as this is blocking a lot and the last update was way too long ago: First of all, sorry for the delay.
I've looked at the code in this state and debugged it again and again trying to find the solution why all menu items are undefined.
Honestly, it was a little discouraging as I still could not really find the cause. I tend to wonder what mess I've did here… 😜
I'm thinking of reverting a lot of it, as the introduction of the symbols made the whole thing complicated – although it may be cleaner code.

So I'm still not sure what to do…
And sorry again for the delay, my time I have to invest here is somewhat limited currently. (If you have ideas or want to debug things, feel free to do so and push into this branch. If you want to move that forwards.)

@tdulcet
Copy link
Collaborator

tdulcet commented Feb 24, 2021

I've looked at the code in this state and debugged it again and again trying to find the solution why all menu items are undefined.
Honestly, it was a little discouraging as I still could not really find the cause. I tend to wonder what mess I've did here… 😜

I am not sure why all the menu items are undefined for you. That is not a problem for me. Note that the menu items have not yet been translated to German and there is currently no fallback for untranslated items (see #5 (comment)). Let me know if there is anything you need me to do.

@rugk
Copy link
Owner Author

rugk commented Feb 24, 2021

That is not a problem for me.

Did you test it with the latest commit in this branch/PR here?

@tdulcet
Copy link
Collaborator

tdulcet commented Feb 25, 2021

Did you test it with the latest commit in this branch/PR here?

Yes, see here:

image

There are still many problems as noted in my comments above, including that the user does not see the effect of the options (compare the above screenshot, with the screenshots here: rugk/awesome-emoji-picker#106 (comment)). I agree that many of the changes in this PR should be reverted, although feel free to do what you think is best.

@rugk
Copy link
Owner Author

rugk commented Feb 28, 2021

Superseded by #17

@rugk rugk closed this Feb 28, 2021
@rugk rugk modified the milestones: 0.5-beta, 0.1 Apr 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
i18n/l10n internationalisation & localisation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants