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

[PORT] Pass Locale info from Activity to Adaptive-Expression and support locale in formatNumber prebuilt functions #2692

Merged
merged 23 commits into from
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ff5700b
add locale in expressions
cosmicshuai Aug 12, 2020
dfae3aa
remove comments
cosmicshuai Aug 12, 2020
1e49373
Merge branch 'master' into shuwan/reworkLocale
boydc2014 Aug 12, 2020
7b61224
add doc for options
cosmicshuai Aug 12, 2020
5ae7a19
Merge branch 'shuwan/reworkLocale' of github.com:microsoft/botbuilder…
cosmicshuai Aug 12, 2020
af1a2b1
move locale info to a seperate file
cosmicshuai Aug 13, 2020
636ffac
Merge branch 'master' into shuwan/reworkLocale
cosmicshuai Aug 13, 2020
937e914
Merge branch 'master' into shuwan/reworkLocale
cosmicshuai Aug 17, 2020
dc0de28
Merge branch 'master' into shuwan/reworkLocale
cosmicshuai Aug 18, 2020
872a594
Merge branch 'master' into shuwan/reworkLocale
cosmicshuai Aug 20, 2020
9dfb0e1
Merge branch 'master' into shuwan/reworkLocale
cosmicshuai Sep 22, 2020
ce0d88e
Merge branch 'main' into shuwan/reworkLocale
cosmicshuai Oct 9, 2020
fb5ff66
eslint --fix
cosmicshuai Oct 9, 2020
277a3a3
swap args postions
cosmicshuai Oct 9, 2020
35b0c67
Merge branch 'main' into shuwan/reworkLocale
cosmicshuai Oct 9, 2020
54d5a30
Merge branch 'main' into shuwan/reworkLocale
cosmicshuai Oct 19, 2020
7361f91
change the determination of default locale
cosmicshuai Oct 19, 2020
640e40b
rewrite determineLocale
cosmicshuai Oct 19, 2020
c317ec2
retrigger
cosmicshuai Oct 19, 2020
e5bc2e7
Merge branch 'main' into shuwan/reworkLocale
cosmicshuai Oct 20, 2020
826dc86
change any to unknown
cosmicshuai Oct 20, 2020
fbd0dd1
refine some code
cosmicshuai Oct 20, 2020
e09808d
retrigger
cosmicshuai Oct 20, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion libraries/adaptive-expressions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"lodash": "^4.17.19",
"lru-cache": "^5.1.1",
"moment": "^2.25.1",
"moment-timezone": "^0.5.28"
"moment-timezone": "^0.5.28",
"d3-format":"^1.4.4"
},
"devDependencies": {
"@microsoft/api-extractor": "^7.7.12",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
* Licensed under the MIT License.
*/

import { formatLocale as d3formatLocale, format as d3format } from 'd3-format';
import { Expression } from '../expression';
import { EvaluateExpressionDelegate, ExpressionEvaluator } from '../expressionEvaluator';
import { ExpressionType } from '../expressionType';
import { FunctionUtils } from '../functionUtils';
import { ReturnType } from '../returnType';
import { Options } from '../options';
import { localeInfo } from '../localeInfo';

/**
* Format number into required decimal numbers.
Expand All @@ -27,35 +30,40 @@ export class FormatNumber extends ExpressionEvaluator {
* @private
*/
private static evaluator(): EvaluateExpressionDelegate {
return FunctionUtils.applyWithError((args: any[]): any => {
let value: any = null;
return FunctionUtils.applyWithOptionsAndError((args: unknown[], options: Options): {
value: unknown;
error: string;
} => {
let value: unknown = null;
let error: string;
const number = args[0];
const precision = args[1];
const locale = args.length > 2 ? args[2] : 'en-us';
let locale = options.locale;
locale = FunctionUtils.determineLocale(args, 3, locale);
if (typeof number !== 'number') {
error = `formatNumber first argument ${number} must be a number`;
} else if (typeof precision !== 'number') {
error = `formatNumber second argument ${precision} must be a number`;
} else if (locale && typeof locale !== 'string') {
error = `formatNubmer third argument ${locale} is not a valid locale`;
} else {
// NOTE: Nodes toLocaleString and Intl do not work to localize unless a special version of node is used.
// TODO: In R10 we should try another package. Numeral and d3-format have the basics, but no locale specific.
// Numbro has locales, but is optimized for the browser.
value = number.toLocaleString(locale, {
minimumFractionDigits: precision,
maximumFractionDigits: precision,
});
const fixedNotation = `,.${precision}f`;
const roundedNumber = this.roundToPrecision(number, precision);
const formatLocale = localeInfo[locale];
if (formatLocale !== undefined) {
value = d3formatLocale(formatLocale).format(fixedNotation)(roundedNumber);
} else {
value = d3format(fixedNotation)(roundedNumber);
}
}

return { value, error };
});
}

/**
* @private
*/
private static roundToPrecision = (num: number, digits: number): number =>
Math.round(num * Math.pow(10, digits)) / Math.pow(10, digits);

private static validator(expr: Expression): void {
FunctionUtils.validateOrder(expr, [ReturnType.String], ReturnType.Number, ReturnType.Number);
}
Expand Down
41 changes: 41 additions & 0 deletions libraries/adaptive-expressions/src/functionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,33 @@ export class FunctionUtils {
};
}

/**
* Generate an expression delegate that applies function after verifying all children.
* @param func Function to apply.
* @param verify Function to check each arg for validity.
* @returns Delegate for evaluating an expression.
*/
public static applyWithOptionsAndError(
func: (arg0: unknown[], options: Options) => { value: unknown; error: string },
verify?: VerifyExpression
): EvaluateExpressionDelegate {
return (expression: Expression, state: MemoryInterface, options: Options): ValueWithError => {
let value: unknown;
let error: string;
let args: unknown[];
({ args, error } = FunctionUtils.evaluateChildren(expression, state, options, verify));
if (!error) {
try {
({ value, error } = func(args, options));
} catch (e) {
error = e.message;
}
}

return { value, error };
};
}

/**
* Generate an expression delegate that applies function on the accumulated value after verifying all children.
* @param func Function to apply.
Expand Down Expand Up @@ -551,6 +578,20 @@ export class FunctionUtils {
}, verify);
}

/**
*
* @param args An array of arguments.
* @param locale A locale string
* @param maxArgsLength The max length of a given function.
*/
public static determineLocale(args: unknown[], maxArgsLength: number, locale = 'en-us'): string {
if (args.length === maxArgsLength && typeof args[maxArgsLength - 1] === 'string') {
locale = args[maxArgsLength - 1] as string;
cosmicshuai marked this conversation as resolved.
Show resolved Hide resolved
}

return locale;
}

/**
* Timestamp formatter, convert C# datetime to moment js format.
* @param formatter C# datetime format
Expand Down
1 change: 1 addition & 0 deletions libraries/adaptive-expressions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ export {
} from './builtinFunctions';
export * from './functionUtils';
export * from './returnType';
export * from './localeInfo';
export * from './triggerTrees';
Loading