Expensify / App

Welcome to New Expensify: a complete re-imagination of financial collaboration, centered around chat. Help us build the next generation of Expensify by sharing feedback and contributing to the code.
https://new.expensify.com
MIT License
3.34k stars 2.77k forks source link

[HOLD for payment 2022-02-01] $1000 - IOU - Decimal separator not updated when set to Spanish on some screens #6427

Closed kavimuru closed 2 years ago

kavimuru commented 2 years ago

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Action Performed:

  1. Open app and login
  2. Go to profile settings and set language to Español
  3. Request money from any contact in any currency

Expected Result:

All money amounts are localized to Spanish, comma as decimal separator. Eg: C$ 23,85

Actual Result:

Decimal separators are not localized in some screens. They are shown as English, in dots. Eg: C$ 23.85

Workaround:

Unknown

Platform:

Where is this issue occurring?

Version Number: 1.1.16-1 Reproducible in staging?: Yes Reproducible in production?: yes Logs: https://stackoverflow.com/c/expensify/questions/4856 Notes/Photos/Videos:

Expensify/Expensify Issue URL:

Issue reported by: Applause Slack conversation:

View all open jobs on GitHub

MelvinBot commented 2 years ago

Triggered auto assignment to @roryabraham (Engineering), see https://stackoverflow.com/c/expensify/questions/4319 for more details.

MelvinBot commented 2 years ago

Triggered auto assignment to @bfitzexpensify (External), see https://stackoverflow.com/c/expensify/questions/8582 for more details.

bfitzexpensify commented 2 years ago

Posted to Upwork - job

MelvinBot commented 2 years ago

Triggered auto assignment to @parasharrajat (Exported)

MelvinBot commented 2 years ago

Triggered auto assignment to @Luke9389 (Exported), see https://stackoverflow.com/c/expensify/questions/7972 for more details.

anthony-hull commented 2 years ago

Proposal

I will format the value of this.state.amount https://github.com/Expensify/App/blob/96ba12da431936b19c80eb895f9531ee44039e71/src/pages/iou/steps/IOUAmountPage.js#L196 in WithLocalise

props.numberFormat(
        this.state.amount
        {options},
    );
parasharrajat commented 2 years ago

@anthony-hull Thanks for the proposal. How would you solve the first screen issue?

I feel that it would inconsistent for the Request Money screen if the user types . from the keypad and , is added on the screen. What do you think @Luke9389 ?

railway17 commented 2 years ago

I am submitting this proposal because I've got an email that was allowed to be hired for another jobs by this pull request approval

1. Replication

I was able to reproduce this issue in Web and Android versions and absolutely sure that this issue will also occur on other platforms. I am attaching my replication screenshots to let you understand easier (but screenshots are displaying some HTTP requests). web-issue android-issue

2. Problem

Mainly, we have 2 problems that are related to this issue.

Requested {currency}{number} from {name} string is also taken from storage, but there is not any conversion module or Localization keys for these words.

I think it is not recommended way to save the values with formatted and is better to reformat the values before displaying in the frontend (after getting the values from storage).

3. Solution.


//format saving values to normal number format


     <Button
         success
         style={[styles.w100, styles.mt5]}

- **PROBLEM 2:** Let me describe how to fix with the `ReportTransaction` component as an example.
Because we can use the same conversion method in the 3 parts, I am defining the conversion method in the `libs/reportUtils.js`. And will display the converted values by using this conversion method.
```diff
//ReportTransaction.js
+ import compose from '../libs/compose';
+ import withLocalize from './withLocalize';
+ import * as ReportUtils from './../libs/reportUtils'
---
         <Text style={[styles.chatItemMessage]}>
-              {this.props.action.message[0].text}
+             {ReportUtils.changeNumberFormatFromMessage(this.props.action.message[0].text, this.props.numberFormat)}
          </Text>

---
-export default withOnyx({
-    transactionsBeingRejected: {
-        key: ONYXKEYS.TRANSACTIONS_BEING_REJECTED,
-    },
-})(ReportTransaction);
+export default compose(
+        withLocalize,
+        withOnyx({
+        transactionsBeingRejected: {
+            key: ONYXKEYS.TRANSACTIONS_BEING_REJECTED,
+        },
+    }),
+)(ReportTransaction);

// libs/reportUtils.js

+  function changeNumberFormatFromMessage(messageText, changeNumberFormat) {
+     const numRegex = new RegExp(/[\d,]+[\.\d+]*/g);
+     const matches = messageText.match(numRegex)
+   if (matches) {
+        const matchStrNum = matches[0].replace(/,/g, '');
+       const formattedNumber = changeNumberFormat(matchStrNum);
+        messageText = messageText.replace(numRegex, formattedNumber);
+    }
+    return messageText
+ }
railway17 commented 2 years ago

Well.. @bfitzexpensify

I tried to submit my proposal in upwork but got a strange issue. It is not loaded while loading send proposal page. Sometimes it is loaded but Terms form is different from another jobs and can't submit.

image

Maybe, is it related open jobs that I worked for Magnifying icon issue? Even though I've got the pull request approval and email, still can't send new proposal?

Please help me how can I do

parasharrajat commented 2 years ago

Looking at the proposals....

roryabraham commented 2 years ago

@railway17 I really liked your thorough summary of the problems, but wasn't a big fan of your proposal for a few reasons:

  1. There was no written summary of your proposed solutions. As a reviewer, it's a lot of work to evaluate proposals by reviewing code snippets. The goal of these proposals is to discuss and agree upon a solution before diving into any code review.
  2. You also didn't address the UX problem that @parasharrajat pointed out in this comment.
  3. It could have been more concise.

That said, here are some high-level goals we should strive for in a solution for this issue:

  1. We should strive to solve this issue on all screens, such that there is no place in our app where we display non-localized floating-point numbers.
  2. We should not store any numeric values within formatted strings in Onyx, because that would prevent us from being able to localize those numbers easily. i.e: don't store Requested $10.50 from Rajat. Instead, just store the number 10.50, and localize it before displaying it.
  3. Numbers should always be stored in US format, regardless of the format they were entered in.
  4. We should leverage generic functions to localize floating point numbers on the front-end.
  5. The BigNumberPad and IOUAmountPage should be localized as well, such that if the locale is es then a user cannot manually enter a ., just as they cannot manually enter a , in the components' current forms.
Luke9389 commented 2 years ago

I think we're still looking for a proposal that can encapsulate the points that @roryabraham has mentioned above. Maybe we need a localNumber component or something like that to use.

railway17 commented 2 years ago

3. concise Thank you for your explain, @roryabraham Let me rewrite the solution with the summary. As I mentioned, we need to resolve 2 kinds of problems at this issue.

  • Resolve the WRITING and DISPLAYING issue in the IOUAmountPage and BigNumberPad. It can be resolved by updating the validateAmount and stripCommaFromAmount methods in IOUAmountPage.js

Thank you.

parasharrajat commented 2 years ago

I agree with @roryabraham points. Although the proposal touches most of the code but does not explain it clearly. I also think,

  1. we don't make use of locales in code directly,
     if (this.props.preferredLocale == 'es') {
    +            enFormattedAmount = amount.replace(/\./g, '').replace(/,/g, '.');
    +        }

    we don't do this. The app is not specifically configured for Spanish. There could be many languages. The solution should work with any language that we have or add in the future. for this purpose, we have withLocalize HOC.

The problem might occur when they enter long numbers such as 123. 234,56, 34. 252. 323,98... in IOUAmountPage. I think we should allow type , they want to enter float value. I don't think it is not recommended way to click the , key if they want to enter . or click . if they want to enter ,.

I think, we are not concerned with the number of segments ATM. Only the decimal part matters. it's totally fine that if you enter 123456.34 and it is shown as 123,456.34 in other parts of the app. But if the decimal is presented as , instead of . it becomes an issue. For E.g. Spanish user typing 123.456 then it's not ~~ 123 instead he would parse it as 123 thousand 4 hundred fifty-six but in real our app would take that as 1 hundred twenty-three point 46. This is a big transactional issue.

parasharrajat commented 2 years ago

We should not store any numeric values within formatted strings in Onyx, because that would prevent us from being able to localize those numbers easily. i.e: don't store Requested $10.50 from Rajat. Instead, just store the number 10.50, and localize it before displaying it. For the Requested {number} from {name} string issue, it wasn't reported in this issue.

I think we had another issue to localize IOU strings. cc: @iwiznia

iwiznia commented 2 years ago

I agree that onyx should not save localized data. Not sure what other issue you are referring to, I think you might be thinking about the messages that are generated in the backend? (which we are not localizing now)

railway17 commented 2 years ago

I agree that onyx should not save localized data. Not sure what other issue you are referring to, I think you might be thinking about the messages that are generated in the backend? (which we are not localizing now)

Yes, I think these messages are generated from the backend.

iwiznia commented 2 years ago

Any texts coming from the server can remain unlocalized, since we yet have to add support for localization in the server, for now only the frontend has the framework to localize things.

railway17 commented 2 years ago

So, I have also reported converting the text before displaying it in the frontend (after getting data from Onyx).

bfitzexpensify commented 2 years ago

@parasharrajat what's the status here? Is here @railway17's proposal good, does it need further work, or shall I increase the price for this issue?

parasharrajat commented 2 years ago

@bfitzexpensify First we need to redefine the scope of this issue. Based on the latest comment https://github.com/Expensify/App/issues/6427#issuecomment-982662634, What should be the new expected behaviour here? Not sure whom to tag here but @Luke9389 Could you please point out which part do we need to fix out of the posted screens in the description?

Secondly, Railways proposal is still lacking proper details. He would need to rephrase his proposal after we refine the scope here.

There is an open question here https://github.com/Expensify/App/issues/6427#issuecomment-979227780. I am not sure what is decided for that.

I don't think it is not recommended way to click the , key if they want to enter . or click . if they want to enter ,.

Not sure what is proposed here.

roryabraham commented 2 years ago

Chatted with @Luke9389 1:1 and he'll be the CME on this issue going forward. Thanks Luke!

Luke9389 commented 2 years ago

Question, are we sure we want to report USD using commas? It seems like the notation for the amount should be set by the currency, not by the language. If we were requesting a number of pesos, then I'd expect a comma. Right?

iwiznia commented 2 years ago

It seems like the notation for the amount should be set by the currency, not by the language. If we were requesting a number of pesos, then I'd expect a comma. Right?

Nope. The decimal separator is tied to the language you are writing in, not the language that's used in the country were the currency was created.

bfitzexpensify commented 2 years ago

Haven't had any action here for a while. Reposted the job here (editing in Upwork is broken at the moment) and raised price to $1000

railway17 commented 2 years ago

Hi, @iwiznia, @bfitzexpensify If you still dislike my proposal, I would like to confirm again.

  1. I am writing the number conversion below, please make sure it is correct. 123, 456, 678.89 -> 123. 456. 678,89 12.36->12,36

  2. While entering a comma in the IOU, it should be displayed as a dot instantly unless it is float point? So even though I typed 123, 456, 235, then it should be displayed 123. 456. 235? And it should not be displayed as comma even though user typed a dot in the float point? ex: typed 12.25 -> 12.25.

For question 2, please make sure whether it should be instant conversion or not.

Actually, I think nobody knows clearly about this action logic here.

parasharrajat commented 2 years ago

Luke is probably on leave but here are a few questions before I review the proposals. https://github.com/Expensify/App/issues/6427#issuecomment-989201104

iwiznia commented 2 years ago

IMO the expected behavior is that the keyboard and the amount show , instead of . if your language's decimal separator is ,. Not sure about the keyboard, but for showing the amount correctly, that is all handled by the existing number formatting functions.

railway17 commented 2 years ago

IMO the expected behavior is that the keyboard and the amount show , instead of . if your language's decimal separator is ,. Not sure about the keyboard, but for showing the amount correctly, that is all handled by the existing number formatting functions.

Btw, it doesn't a little complex. Because we might use , keyboard while sending messages in chat, it is a little confusable. Let's assume that we are sending message like Hi, XXX! I've sent $12, 322, 23.12 to you in the chatbox. Even though we are discussing about IOU board, we can assume this case. In this case, there maybe some other complex problems if you have to change the , and . in realtime.

Because of this reason, I was asking to make sure logic again.

iwiznia commented 2 years ago

I am very confused by your message. This issue applies to where we display or input an amount. If you are writing text on the chat, we won't do anything and send the message as you wrote them, without any processing or change.

songlang1994 commented 2 years ago

I submit my proposal try to summarize this issue. Hope to make it more clear.

We're discussing the numbers format, especially the decimal point, which can appear in several different places.

1 Numbers that in internal storage

Numbers for internal storage we use a dot (".") as decimal point, never add any thousandth separator, and only keep the significant digits.

Examples:

"1"
"23456.99"
"19940910.996996"

This format is able to be parsed by JavaScript engine or other decimal libs, such as bignumber.js. I'd like to call this format as "normalized format".

2 Numbers that displaying on UI

Numbers should be transformed to the localized format before displaying on UI.

Examples:

"1200.5"  -> "1,200.50" (English)
"1200.5"  -> "1.200,50" (Spanish)

3 Numbers that user inputing

3.1 Digits on the virtual keypad

Digits (0-9) and the decimal point (".") should be displayed in localized format on the keypad. So the keypad in Spanish environment should looks like:

|------|------|------|
| 1(1) | 2(2) | 3(3) |
|------|------|------|
| 4(4) | 5(5) | 6(6) |
|------|------|------|
| 7(7) | 8(8) | 9(9) |
|------|------|------|
| ,(.) | 0(0) | <(\b)|
|------|------|------|

The leading character is used to display on UI. The character in brackets will be the input event value when user touch the key, they should be stable regardless of any language environment. Therefore, when a Spanish user touch the "," key on the keypad, the app still recognizes that the user has input a "." character.

3.2 An input sequence example

User Input UI Display (English) UI Display (Spanish) Internal Storage
1 1 1 1
12 12 12 12
120 120 120 120
1200 1,200 1.200 1200
1200. 1,200. 1.200, NaN
1200.5 1,200.5 1.200,5 1200.5
1200.50 1,200.50 1.200,50 1200.5

3.3 Input from hardware keyboard

This above way is good enough for the mobile app which uses the virtual keypad. But for the desktop app, users will input numbers directly from the hardware keyboard. In this case, we should have 2 options:

  1. Option 1: Display a help message that ask users to input decimal point using "." instead of any other characters.
  2. Option 2: Add a hook which transform the localized decimal point (e.g. "," in Spanish) that the user has input to the normalized one (".") before handling the input event. This way need an API to detect the decimal point character of current language.

I'd prefer the option 2 if it's possible to implemente since it's a more localized approach.

parasharrajat commented 2 years ago

Great explanation @songlang1994.

The leading character is used to display on UI. The character in brackets will be the input event value when users touch the key, they should be stable regardless of any language environment. Therefore, when a Spanish user touches the "," key on the keypad, the app still recognizes that the user has input a "." character.

This is what is needed for this issue.

3.3 Input from hardware keyboard

I don't think we need to worry about this. Hardware keyboards or virtual keyboards use standard keys and I think they will work just fine.

Do you have a proposal to fix the problem explained above?

songlang1994 commented 2 years ago

@parasharrajat My proposal.

Add translation for BigNumberPad. Currently I think just translate the decimal point is well enougth to match the requirement.

  // File: src/languages/en.js
+ bigNumberPad: {
+   decimalPoint: '.',
+ },

  // File: src/languages/es.js
+ bigNumberPad: {
+   decimalPoint: ',',
+ },

  // File: src/components/BigNumberPad.js
  <Button
      key={column}
      style={[styles.flex1, marginLeft]}
-     text={column}
+     text={column === '.' ? this.props.translate(`bigNumberPad.decimalPoint`) : column}
      onLongPress={() => this.handleLongPress(column)}

Localize amount before passing into TextInputAutoWidth

  // File: src/pages/iou/steps/IOUAmountPage.js

+ get localizedInputingAmount() {
+     if (!this.state.amount) {
+         return '';
+     }
+
+     const endsWithDecimalPoint = this.state.amount.endsWith('.');
+     if (endsWithDecimalPoint) {
+         const localized = this.props.numberFormat(this.state.amount, {useGrouping: false, minimumFractionDigits: 1});
+         return localized.slice(0, -1);
+     }
+
+     const fraction = this.state.amount.split('.')[1];
+     const fractionDigits = fraction ? fraction.length : 0;
+     return this.props.numberFormat(this.state.amount, {useGrouping: false, minimumFractionDigits: fractionDigits});
+ }

- updateAmount(amount) {
-     if (!this.validateAmount(amount)) {
-         return;
-     }
-
-     this.setState({amount: this.stripCommaFromAmount(amount)});
- }
+ // Handle each input data instead of accepting the whole string from TextInput.
+ // This is a simple implemention which can not handle some special cases,
+ // such as pasting from clipboard.
+ updateAmount({nativeEvent: {data}}) {
+     this.updateAmountNumberPad(data || '<');
+ }

  render() {
    return (
      ...
      <TextInputAutoWidth
          ...
-         onChangeText={this.updateAmount}
+         onChange={this.updateAmount}
-         value={this.state.amount}
+         value={this.localizedInputingAmount}
          ...
      />
      ...
    )
  }

https://user-images.githubusercontent.com/19236594/147811328-06e91447-20c1-4722-8c08-d3232a84d985.mp4

https://user-images.githubusercontent.com/19236594/147811333-deac13f5-4149-44a8-9a70-995124491761.mp4

songlang1994 commented 2 years ago

I don't think we need to worry about this. Hardware keyboards or virtual keyboards use standard keys and I think they will work just fine.

Maybe we do need to transform the decimal point from users input.

In the video "hardware-keyboard.mp4" above, UI shows the localized format number "12,99" but actually I typed:

"1" -> "2" -> "." -> "9" -> "9"

Because I am an English user and I am used to type "." as the decimal point. I think a real Spanish native user will type

"1" -> "2" -> "," -> "9" -> "9"
parasharrajat commented 2 years ago

yeah, that becomes another issue. We are only working on US layouts and expect users to type in US layouts. So if he types , instead of .. This is probably going to be trimmed. but if we types . then it would be converted to ,.

This becomes a complicated problem. We are not only supporting Spanish. We will have other languages as well. So this solution will not be a generic one.

@iwiznia Do you have some comments about it? Also, are we planning to localize the IOU strings as well shown in screenshots?

songlang1994 commented 2 years ago

yeah, that becomes another issue. We are only working on US layouts and expect users to type in US layouts. So if he types , instead of .. This is probably going to be trimmed. but if we types . then it would be converted to ,.

It's not really so complicated. The process of handling keyboard input is very clear:

  1. Only accept digit "0-9" (localized format) and decimal point (localized format) but ignore any other chars from users input .
  2. Transform localized format digits to standard format.
  3. The rest of steps are exact same as virtual keypad case.

Please take a look at the screenshots first. If it's the expected behavior that you really want to achieve, I have a more general proposal.

TL;DR

1) Instead of translate decimal point mannually, create a new LocaleDigitUtils.js. It will manage the relation between localized format digits and standard format digits.

// File: src/libs/LocaleDigitUtils.js
import invariant from 'invariant';
import _ from 'underscore';

import numberFormat from './numberFormat';

const DECIMAL_SEPARATOR = '.';

function getLocaleDigits(locale) {
    const localeDigits = [];
    for (let i = 0; i <= 9; i++) {
        localeDigits.push(numberFormat(locale, i));
    }

    // The last element is decimal separator
    localeDigits.push(numberFormat(locale, 1.5)[1]);
    return localeDigits;
}

function isValidLocaleDigit(locale, digit) {
    return getLocaleDigits(locale).includes(digit);
}

function toLocaleDigit(locale, digit) {
    const localeDigits = getLocaleDigits(locale);

    if (digit === DECIMAL_SEPARATOR) {
        return localeDigits[10];
    }

    const index = Number(digit);
    invariant(index >= 0 && index <= 9, `${digit} must be in "0~9" or "."`);
    return localeDigits[index];
}

function fromLocaleDigit(locale, localeDigit) {
    const index = _.findIndex(getLocaleDigits(locale), it => it === localeDigit);
    invariant(index > -1, `${localeDigit} is not a valid locale digit`);
    return index < 10 ? index.toString() : DECIMAL_SEPARATOR;
}

export {
    isValidLocaleDigit,
    toLocaleDigit,
    fromLocaleDigit,
};

2) Translate every digits including the decimal point in BigNumberPad.

  // File: src/components/BigNumberPad.js
  <Button
      key={column}
      style={[styles.flex1, marginLeft]}
-     text={column}
+     text={column === '<' ? column : this.props.toLocaleDigit(column)}
      onLongPress={() => this.handleLongPress(column)}

3) Localize amount before pass into TextInputAutoWidth, this step is basically same as I described in https://github.com/Expensify/App/issues/6427#issuecomment-1003303248 except adding a extra transform from hardware keyboard input.

// File: src/pages/iou/steps/IOUAmountPage.js
updateAmount({nativeEvent: {data}}) {
    if (!data) {
        this.updateAmountNumberPad('<');
    } if (this.props.isValidLocaleDigit(data)) {
        // Transform the locale digits that user input to "standard" digits
        this.updateAmountNumberPad(this.props.fromLocaleDigit(data));
    } else {
        // Ignore non-digit keys
    }
}

To give a obvious example, I show an Arabic example in the screenshots. In order to input numbers with hardware keyboard, I've switched my input source to Arbic layout first.

arabic-keyboard-layout

https://user-images.githubusercontent.com/19236594/147856345-77b346fd-562f-44e9-b882-647b4fe1407a.mp4

https://user-images.githubusercontent.com/19236594/147856349-a73b1042-30af-4cba-ac8a-eebcf60aeb33.mp4

parasharrajat commented 2 years ago

I understood what you are trying to achieve here. Basically, something related to what I was thinking to be done for this. It covers the broach picture as well. But I would have to discuss this issue internally to understand what is included in the scope of this issue. This is something I am not yet fully clear about.

Thanks for paying attention to the hardware keyboard as well. I think we will have to manage that as well. So +1 for that.

mdneyazahmad commented 2 years ago

Proposal

I will create a function to get decimal symbol used in the current locale. We can attach this function in the LocaleProvider.

getDecimalSymbol() {
      const formattedNumber = this.props.numberFormat(1.1);
      const localized1 = this.props.numberFormat(1);

      const numberReplaceRegExp = new RegExp(localized1, 'g')
      return formattedNumber.replace(numberReplaceRegExp, '');
  }

1. src/pages/iou/steps/IOUAmountPage.js

It won't make any changes in the current state. It is just to show the formatted number in the ui, internally we will have our numbers in the us locale.

getFormattedAmount(amount) {
        if(!amount) {
            return '';
        }

        let formattedAmount = this.props.numberFormat(amount, {
            useGrouping: false,
        });

        if(amount.endsWith('.')) {
            formattedAmount += this.getDecimalSymbol();
        }

        return formattedAmount;
    }
<TextInputAutoWidth
    inputStyle={styles.iouAmountTextInput}
    textStyle={styles.iouAmountText}
    onChangeText={this.updateAmount}
    ref={el => this.textInput = el}
+  value={this.getFormattedAmount(this.state.amount)}
-  value={this.state.amount}
    placeholder="0"
    keyboardType={CONST.KEYBOARD_TYPE.NUMERIC}
    showSoftInputOnFocus={false}
    inputmode="none"
/>

2. src/components/BigNumberPad.js

I will map the us format locale to the current locale number in the number pad.

https://user-images.githubusercontent.com/77761491/147875344-58d1bc3d-2706-4f86-acc2-055549688f6b.mp4

+import withLocalize, {withLocalizePropTypes} from './withLocalize';
+import compose from '../libs/compose';

getLocalizedChar(char) {

        if(char == '.') {
            return this.getDecimalSymbol();
        }

        if(!isNaN(char)) {
            return this.props.numberFormat(char);
        }

        return char;

    }

<Button
    key={column}
    style={[styles.flex1, marginLeft]}
-  text={column}
+  text={this.getLocalizedChar(column)}
    onLongPress={() => this.handleLongPress(column)}
    onPress={() => this.props.numberPressed(column)}
    onPressIn={ControlSelection.block}
    onPressOut={() => {
        clearInterval(this.state.timer);
        ControlSelection.unblock();
    }}
/>
+ export default compose(withLocalize)(BigNumberPad);
- export default BigNumberPad;
parasharrajat commented 2 years ago

@Luke9389 @iwiznia Awaiting comments https://github.com/Expensify/App/issues/6427#issuecomment-1003364632

Are we planning to localize the IOU strings shown in the screenshots as part of this issue?

mdneyazahmad commented 2 years ago

@parasharrajat As I proposed in my proposal https://github.com/Expensify/App/issues/6427#issuecomment-1003706648 Maybe, this can solve this issue.

  1. Display Number We will store all number in us locale, because Intl.NumberFormat can be used to convert all us locale based number to other.

  2. Get number from keyboard ui We will display the localized number in the numberpad however it will have values in us locale.

  3. Get input from keyboard We will accept the number but when user enter , or . We will check it whether it is a decimal symbol (create helper function to get decimal symbol in current locale) and this will be stored as . in all cases.

iwiznia commented 2 years ago

Are we planning to localize the IOU strings shown in the screenshots as part of this issue?

If you are referring to the comment you see in the chat after doing the request, which is generated in the server, then no.

I don't like @songlang1994's proposal of putting the decimal and thousand separator in the translation files, mostly because they are already defined inside Intl.NumberFormat. The proposal from @mdneyazahmad is a bit better, but kind of odd too, I would recommend using formatToParts and passing a number like 1.10, then grab the part that have type set to decimal.

Regarding the big number pad UI, I agree it should display only the decimal separator of the current locale (grabbed using the method described in the paragraph above).

Regarding the physical keyboard, nothing needs to be done there, if you type a number, it shows up, if you type your locale's decimal separator it will add it and if you type anything else, then nothing should happen.

As for how we return the value to a component using the BigNumberPad, it should always be as a number, not as a string formatted number.

songlang1994 commented 2 years ago

@iwiznia I've post a new implement above which use Intl.NumberFormat to grab the locale digits. See LocaleDiditUtils in this comment. https://github.com/Expensify/App/issues/6427#issuecomment-1003589574

It's just a demo so I didn't optimize the performance yet.

iwiznia commented 2 years ago

Oh, I think I missed that one, sorry. Did not even think about other numbers like arabic. I think that proposal is good, but will let @parasharrajat and @Luke9389 have the final say, since they are assigned.

parasharrajat commented 2 years ago

Thanks for the input @iwiznia.

If you are referring to the comment you see in the chat after doing the request, which is generated in the server, then no.

Based on this, We won't update the message that is coming from the backend in https://user-images.githubusercontent.com/43996225/143068980-41848fd5-bc85-4b2b-9a6b-1315610211ff.jpg.

But some parts of it can be localized such as IOU Preview and Header of Split Page. @songlang1994 your proposal is good for BigNumberpad but could you please add more details for these changes as well.

Thanks for your patience and for helping us understand your proposal.

Luke9389 commented 2 years ago

First of all, I'd like to thank @songlang1994 for helping us clear this up in this comment.

@parasharrajat has a good point. So far, we haven't addressed how we will localize numbers that are coming from the back-end. I'm assuming @songlang1994 would want to use LocaleDigitUtils for this.

@songlang1994, would you be willing to show a proposal for how that would work? Thanks for your patience. I think we're almost there.

songlang1994 commented 2 years ago

The messages from API are actually TEXT rather than numbers. So I think the localization process should be taken care of by the server.

BTW, IOUPreview re-format the amount TEXT by replacing brackets which may not be a nice way. It might be better to contact the back-end team and ask them to handle it.

// File: src/components/ReportActionItem/IOUPreview.js

// The original `cachedTotal` from API was something like "(HK$63.69)".
// `IOUPreview` convert it to "HK$63.69".
const cachedTotal = props.iouReport.cachedTotal ? props.iouReport.cachedTotal.replace(/[()]/g, '') : '';
iou-components text-from-server
parasharrajat commented 2 years ago

yeah, IOUQuote is populated from the backend and we can skip that for now. But we can still change the IOUPreview amount and currency. Look at the IOUBadge for this.

And there is one more screen that needs to be localized the header of image

It would be great if you can submit a finalized proposal in one place so that others are aware of it.

But Overall, I like @songlang1994 's proposal.

cc: @Luke9389

:ribbon: :eyes: :ribbon: C+ reviewed

Luke9389 commented 2 years ago

We're hoping to do localization entirely on the front end. @songlang1994, would you be willing to post the exact response the server is giving? Is it literally returning "Requested HK$12.88 from Test"? If so, maybe we should change the return to be an object, like

{
    type: request,
    amount: 12.58,
    actor: Test
}

That way the front end can localize it more easily.

Luke9389 commented 2 years ago

Yep, I'm in the same boat as @parasharrajat. I'm ready to give the green light to @songlang1994 once a final proposal with all front end screens is submitted. It's OK to skip IOUQuote for now. It's a bit out of scope for this issue.

songlang1994 commented 2 years ago

Is it literally returning "Requested HK$12.88 from Test"?

@Luke9389 Yes. The data from server related to IOUQuote is

"message": [
    {
        "type": "COMMENT",
        "html": "Requested HK$12.58 from Test",
        "text": "Requested HK$12.58 from Test",
        "isEdited": false
    }
],
Click to expand the full response ```jsonc // POST /api?command=Report_GetHistory // [FormData] // // reportID: 85812791 // reportActionsOffset: -1 // reportActionsLimit: 50 // shouldRetry: true // email: songlang1994@outlook.com // authToken: 01BC5DB123416766C2665414B243244FA25B6CC8666E3C5449D3E3B5C83C03C5F4434C55E7B7047F90C951D410624C855E1FBB6D8A682A9E47B3297182318EF3DDDA6E744F669B0BC28C92DC4778548272BBE38C19B7C63EDC046F1A096B9FFB0FC7E0000943BE6117129D0233FDB6CDC15CB21998F0FE4E9DE7F5AB021E353EB97F55F99ECA36D83CA47E3F77A8F517A742D7837F47EE0E1403DF5A37B2DDB188DCF2930D3A36E94D5A5AC4EBC9D4781FDD382075DE91293901CCAE276198AA08CC22ACB0E7D5564EFA5B1AABB2C31C6FFCF5104BF029C8D0A49D3F8FC1F28E9CFD58A239416CE0045A9D5BC3148C1E9DFBF4DC4FB9A21B7464A332E369E2ECDD1DE117B5AA0CC85D618C6D9E48D5E19FC7F3D8C5585A46A3CC8F603634227DB9CFC3B57BFA9FD95FB97B7AC200D6BE08932CDEA7FBF5B293674FE1D8CE4AD514B61B9E50BB328399EFA525D576BB95 // referer: ecash // api_setCookie: false { "history": [ { "person": [ { "type": "TEXT", "style": "strong", "text": "songlang1994@outlook.com" } ], "actorEmail": "songlang1994@outlook.com", "actorAccountID": 11012599, "message": [ { "type": "COMMENT", "html": "Requested HK$12.99 from Test", "text": "Requested HK$12.99 from Test", "isEdited": false } ], "originalMessage": { "IOUReportID": 86311814, "IOUTransactionID": "58630061686784005", "amount": 1299, "comment": "", "currency": "HKD", "type": "create" }, "avatar": "https:\/\/d2k5nsl2zxldvw.cloudfront.net\/images\/avatars\/avatar_3.png", "created": "Dec 31 2021 12:05am PST", "timestamp": 1640937928, "automatic": false, "sequenceNumber": 5, "actionName": "IOU", "shouldShow": true, "clientID": "", "reportActionID": "917927660" }, { "person": [ { "type": "TEXT", "style": "strong", "text": "songlang1994@outlook.com" } ], "actorEmail": "songlang1994@outlook.com", "actorAccountID": 11012599, "message": [ { "type": "COMMENT", "html": "Requested HK$12.66 from Test", "text": "Requested HK$12.66 from Test", "isEdited": false } ], "originalMessage": { "IOUReportID": 86311814, "IOUTransactionID": "58630061686784004", "amount": 1266, "comment": "", "currency": "HKD", "type": "create" }, "avatar": "https:\/\/d2k5nsl2zxldvw.cloudfront.net\/images\/avatars\/avatar_3.png", "created": "Dec 31 2021 12:04am PST", "timestamp": 1640937899, "automatic": false, "sequenceNumber": 4, "actionName": "IOU", "shouldShow": true, "clientID": "", "reportActionID": "917927544" }, { "person": [ { "type": "TEXT", "style": "strong", "text": "songlang1994@outlook.com" } ], "actorEmail": "songlang1994@outlook.com", "actorAccountID": 11012599, "message": [ { "type": "COMMENT", "html": "Requested HK$12.88 from Test", "text": "Requested HK$12.88 from Test", "isEdited": false } ], "originalMessage": { "IOUReportID": 86311814, "IOUTransactionID": "58630061686784003", "amount": 1288, "comment": "", "currency": "HKD", "type": "create" }, "avatar": "https:\/\/d2k5nsl2zxldvw.cloudfront.net\/images\/avatars\/avatar_3.png", "created": "Dec 31 2021 12:03am PST", "timestamp": 1640937829, "automatic": false, "sequenceNumber": 3, "actionName": "IOU", "shouldShow": true, "clientID": "", "reportActionID": "917927269" }, { "person": [ { "type": "TEXT", "style": "strong", "text": "songlang1994@outlook.com" } ], "actorEmail": "songlang1994@outlook.com", "actorAccountID": 11012599, "message": [ { "type": "COMMENT", "html": "Requested HK$12.58 from Test", "text": "Requested HK$12.58 from Test", "isEdited": false } ], "originalMessage": { "IOUReportID": 86311814, "IOUTransactionID": "58630061686784002", "amount": 1258, "comment": "", "currency": "HKD", "type": "create" }, "avatar": "https:\/\/d2k5nsl2zxldvw.cloudfront.net\/images\/avatars\/avatar_3.png", "created": "Dec 30 2021 11:38pm PST", "timestamp": 1640936298, "automatic": false, "sequenceNumber": 2, "actionName": "IOU", "shouldShow": true, "clientID": "", "reportActionID": "917920802" }, { "person": [ { "type": "TEXT", "style": "strong", "text": "songlang1994@outlook.com" } ], "actorEmail": "songlang1994@outlook.com", "actorAccountID": 11012599, "message": [ { "type": "COMMENT", "html": "Requested HK$12.58 from Test", "text": "Requested HK$12.58 from Test", "isEdited": false } ], "originalMessage": { "IOUReportID": 86311814, "IOUTransactionID": "58630061686784001", "amount": 1258, "comment": "", "currency": "HKD", "type": "create" }, "avatar": "https:\/\/d2k5nsl2zxldvw.cloudfront.net\/images\/avatars\/avatar_3.png", "created": "Dec 30 2021 11:32pm PST", "timestamp": 1640935967, "automatic": false, "sequenceNumber": 1, "actionName": "IOU", "shouldShow": true, "clientID": "", "reportActionID": "917920460" }, { "actionName": "CREATED", "created": "Dec 20 2021 10:51pm PST", "timestamp": 1640069488, "avatar": "https:\/\/d2k5nsl2zxldvw.cloudfront.net\/images\/avatars\/avatar_3.png", "message": [ { "type": "TEXT", "style": "strong", "text": "__fake__" }, { "type": "TEXT", "style": "normal", "text": " created this report" } ], "person": [ { "type": "TEXT", "style": "strong", "text": "__fake__" } ], "automatic": false, "sequenceNumber": 0, "shouldShow": true } ], "jsonCode": 200, "requestID": "6c93e6470c2e3b4c-SJC" } ```