antipalos / antipalos.github.io

Σελίδες γνώσης
4 stars 5 forks source link

#11 Localize number formatting #19

Closed vsubhuman closed 6 years ago

vsubhuman commented 6 years ago

Features

  1. Implemented locale detection in browser
  2. Implemented locale reading from the loc URL parameter
  3. Implemented "locale context" of multiple "available" locales sorted by priority
  4. Implemented locale selector (as a combobox with the formatting example)
  5. Implemented storing the locale in cookies if selected manually from the selector or if specified in the loc! parameter
  6. Percent inputs are also under Cleave now in order to pre-format the decimal point properly
  7. Implemented fallback mapping for non-arabic numeral systems (no global support yet)

Improvements

  1. Extracted "standalone" function into separate Utils module
  2. Added (auto-calculated) scale property to parameters in the context
vsubhuman commented 6 years ago

Ok, this is how it works exactly:

1. We use "en" as the default locale.

This is just taken as an axiom, by definition, and not subjected to any discussions for now.

2. Our original goal is to detect the browser locale and to use it by default for formatting.

This is solved with the detectBrowserLocale function.

3. Next logical problem - is that people may not want to use localised formatting and would be happier using the default english formatting.

In order to give people the option to fall back to default hardcoded locale (that would be equal for anyone) - we init default locale along with the detected one and we add combo-box selector that allows user to change back to default locale at any time.

4. Next logical problem - is that people would be annoyed if they had to change back to default locale every time they visit.

So whenever user actively manually changes a locale in the selector - it is stored in a cookie. Whenever the website is launched - cookies are searched for a stored locale and this locale is prioritised over the browser locale. But the browser locale is still available to the user to be selected at any time.

5. Next logical problem - is that the detectBrowserLocale function is not guaranteed to give a valid (or any) result at all.

This is a historical problem of the browser-universe, and this is exactly why the function itself looks like this. We solve this problem by providing and option to manually specify any preferred locale as a url parameter loc. For example, like this:

  1. .../cardano-calculator/?loc=de (German - Germany)
  2. .../cardano-calculator/?loc=en-jm (English - Jamaica)
  3. .../cardano-calculator/?loc=es-bo (Spanish - Bolivia)

The full list of locales may be found here: http://wiki.freepascal.org/Language_Codes. Either Language Code or LCID string may be used as parameter value. The region code (after the dash) actually should not usually change the number format, but it is generally supported too. It will come in handy when we will reuse the same mechanisms for i18n.

The locale specified in the URL is prioritised over both the browser locale and default locale. So even if the browser detects locale wrong - user may override it with the URL parameter. Note that standard URL format allows user to pass multiple values, like this: /cardano-calculator/?loc=de&loc=es&loc=ja. We process such a case and just show user all these locales as available for selection in the combo-box selector. The parameter values are prioritised in the order they are specified in the URL, so in the mentioned example - de will be selected by default.

Note: that locale specified in the URL is also displayed in the selector, so user can still at any point manually switch to any other locale (browser or default), or back. This means that user can switch to any other locale and then switch back to the URL locale - and the rule about manually selected locale being stored in cookies is still working. This means that a user may save the URL locale by switching to some other locale, and then switching back.

6. Next logical problem - it would be fairly hard to explain previous scenario of storing a URL locale in cookies for a user that experiences problems with the locale detecting.

Specifically to solve the weirdness of the previous case we additionally read URL parameter loc!. This parameter differs from previous one only by the exclamation mark on the end and this parameter means: "use this locale AND store it as the default one". First specified valid locale value in this parameter will be stored in cookies, and available at the next launch. Values of this parameter are prioritised over the loc values, so they will appear in the selector first.

For example, opening a url like this: /cardano-calculator/?loc!=de&loc!=es&loc=ja will lead to the de locale being stored in cookies, and then all three locales (along with the browser locale and default locale) to be available in the selector.

Summary

In general the solution with the locales may be described like this:

  1. At launch - we build "a context" of all available locales
  2. Available locales are calculated like this (in order of priority):
    1. All locales from the loc! URL parameters (in order of appearance)
    2. All locales from the loc URL parameters (in order of appearance)
    3. Locale from cookies
    4. Locale detected in browser
    5. Default locale (en)
  3. All invalid locales are ignored
  4. All duplicates are removed, so only unique locales are preserved in the context in order of priority - e.g. if you specify the en locale in the URL - it will appear above the browser-detected locale and the default locale will be ignored at all (as a duplicate).
  5. First value from the loc! parameter (if present) is written into cookies as default locale
  6. Example formatting is generated for each locale and shown along it in the selector
  7. When user selects a value in the selector - formatting is switched dynamically and new value is written into cookies

Examples

Browser locale is ru. User opens url .../.

He sees the page in ru. List of locales for him is: [ru (selected), en].

The same user opens url .../?loc!=de&loc!=es&loc=42&loc=ja.

In the result user will see the page with the de formatting. In the selector he will see this list of locales: [de (selected), es, ja, ru, en]. Invalid value 42 is ignored (with a log message). de is stored in cookies as default value.

The same user opens url .../.

He sees the page in de. List of locales is: [de (selected), ru, en].

The same user opens url .../loc=ja.

He sees the page in ja. List of locales is: [ja (selected), de, ru, en].

The same user opens url .../loc!=en.

He sees the page in en. List of locales is: [en (selected), de, ru]. Default en is ignored since it's a duplicate. en is stored in cookies as default value.

The same user opens url .../.

He sees the page in en. List of locales is: [en (selected), ru]. The de is lost, since it's not in the cookies anymore. The en is prioritised over the ru this time, since it's now in the cookies.

Non-arabic numeral systems

Some locales actualy use non-arabic numbers (for example, bn - Bengali). Technically it is possible for us to support proper formatting and display for such numbers, but this would look inconsistent with the rest of the website for now, since there's still a lot of numbers in other parts (like the "formula") that are hardcoded as arabic. Some day we may implement full non-arabic support, but only if we manage to locale-format all the numbers on the website.

For now - all the non-arabic digit-characters are automatically converted to arabic digits.

Original Bengali formatting: Bengali formatting

How it looks in our case: Fall-back to arabic

Note, also: that "weird" (non-classical) ordering is preserved. This is by-design feature, so that at least people would be familiar with the number ordering. If not satisfied with the digits being replaced by arabic, but the original order being preserved - user may always switch to the default or any other suitable locale.