type2docfx
Version:
A tool to convert json format output from TypeDoc to universal reference model for DocFx to consume.
282 lines (241 loc) • 12.1 kB
text/typescript
/**
* @module botbuilder-ai
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { Middleware, TurnContext, ActivityTypes } from 'botbuilder';
import * as DateTimeRecognizers from '@microsoft/recognizers-text-date-time';
import * as moment from 'moment';
export interface LocaleConverterSettings {
toLocale: string,
fromLocale?: string,
getUserLocale?: (context: TurnContext) => string,
setUserLocale?: (context: TurnContext) => Promise<boolean>
}
/**
* The LocaleConverter converts all locales in a message to a given locale.
*/
export class LocaleConverter implements Middleware {
private localeConverter: ILocaleConverter;
private fromLocale: string | undefined;
private toLocale: string;
private getUserLocale: ((context: TurnContext) => string) | undefined;
private setUserLocale: ((context: TurnContext) => Promise<boolean>) | undefined;
public constructor(settings: LocaleConverterSettings) {
this.localeConverter = new MicrosoftLocaleConverter();
this.toLocale = settings.toLocale;
this.fromLocale = settings.fromLocale;
this.getUserLocale = settings.getUserLocale;
this.setUserLocale = settings.setUserLocale;
}
/// Incoming activity
public async onTurn(context: TurnContext, next: () => Promise<void>): Promise<void> {
if (context.activity.type != ActivityTypes.Message) {
return next();
}
if (this.setUserLocale != undefined) {
let changedLocale = await this.setUserLocale(context);
if (changedLocale) {
return Promise.resolve();
}
}
return this.convertLocalesAsync(context)
.then(() => next());
}
private async convertLocalesAsync(context: TurnContext): Promise<void> {
let message = context.activity;
let fromLocale: string;
if (this.fromLocale != undefined) {
fromLocale = this.fromLocale;
} else if (this.getUserLocale != undefined) {
fromLocale = this.getUserLocale(context);
} else {
fromLocale = 'en-us';
}
return this.localeConverter.convert(message.text, fromLocale, this.toLocale)
.then(result => {
message.text = result;
return Promise.resolve();
});
}
public getAvailableLocales(): Promise<string[]> {
return this.localeConverter.getAvailableLocales()
.then(result => Promise.resolve(result));
}
}
interface ILocaleConverter {
isLocaleAvailable(locale: string): boolean;
convert(message: string, fromLocale: string, toLocale: string): Promise<string>;
getAvailableLocales(): Promise<string[]>;
}
class MicrosoftLocaleConverter implements ILocaleConverter {
mapLocaleToFunction: { [id: string] : DateAndTimeLocaleFormat } = {};
constructor() {
this.initLocales();
}
private initLocales() {
let yearMonthDay = new DateAndTimeLocaleFormat('hh:mm', 'yyyy-MM-dd');
let dayMonthYear = new DateAndTimeLocaleFormat('hh:mm', 'dd/MM/yyyy');
let monthDayYEar = new DateAndTimeLocaleFormat('hh:mm', 'MM/dd/yyyy');
let yearMonthDayLocales = [ "en-za", "en-ie", "en-gb", "en-ca", "fr-ca", "zh-cn", "zh-sg", "zh-hk", "zh-mo", "zh-tw" ];
yearMonthDayLocales.forEach(locale => {
this.mapLocaleToFunction[locale] = yearMonthDay;
});
let dayMonthYearLocales = [ "en-au", "fr-be", "fr-ch", "fr-fr", "fr-lu", "fr-mc", "de-at", "de-ch", "de-de", "de-lu", "de-li" ];
dayMonthYearLocales.forEach(locale => {
this.mapLocaleToFunction[locale] = dayMonthYear;
});
this.mapLocaleToFunction["en-us"] = monthDayYEar;
}
isLocaleAvailable(locale: string): boolean {
return !(typeof this.mapLocaleToFunction[locale] === "undefined")
}
private extractDates(message: string, fromLocale:string): TextAndDateTime[] {
let fndDates: string[];
let culture = DateTimeRecognizers.Culture.English;
if (fromLocale.startsWith("fr")) {
culture = DateTimeRecognizers.Culture.French;
} else if (fromLocale.startsWith("pt")) {
culture = DateTimeRecognizers.Culture.Portuguese;
} else if (fromLocale.startsWith("zh")) {
culture = DateTimeRecognizers.Culture.Chinese;
} else if (fromLocale.startsWith("es")) {
culture = DateTimeRecognizers.Culture.Spanish;
} else if(!fromLocale.startsWith("en")) {
throw new Error("Unsupported from locale");
}
let model = new DateTimeRecognizers.DateTimeRecognizer(culture).getDateTimeModel();
let results = model.parse(message);
let foundDates: TextAndDateTime[] = [];
results.forEach(result => {
let curDateTimeText: TextAndDateTime;
let momentTime: Date;
let momentTimeEnd: Date;
let foundType: string;
let resolutionValues = result.resolution["values"][0];
let type = result.typeName.replace('datetimeV2.', '');
if (type.includes('range')) {
if (type.includes('date') && type.includes('time')) {
momentTime = moment(resolutionValues["start"]).toDate();
momentTimeEnd = moment(resolutionValues["end"]).toDate();
foundType = 'datetime';
} else if (type.includes('date')) {
momentTime = moment(resolutionValues["start"]).toDate();
momentTimeEnd = moment(resolutionValues["end"]).toDate();
foundType = 'date';
} else { // Must be a time-only result with no date
momentTime = new Date();
momentTime.setHours(parseInt(String(resolutionValues['start']).substr(0, 2)));
momentTime.setMinutes(parseInt(String(resolutionValues['start']).substr(3, 2)));
momentTimeEnd = new Date();
momentTimeEnd.setHours(parseInt(String(resolutionValues['end']).substr(0, 2)));
momentTimeEnd.setMinutes(parseInt(String(resolutionValues['end']).substr(3, 2)));
foundType = 'time';
}
curDateTimeText = {
text: new RegExp(`\\b${result.text}\\b`, "gi"),
dateTimeObj: momentTime,
endDateTimeObj: momentTimeEnd,
type: foundType,
range: true
}
} else {
if (type.includes('date') && type.includes('time')) {
momentTime = moment(resolutionValues["value"]).toDate();
foundType = 'datetime';
} else if (type.includes('date')) {
momentTime = moment(resolutionValues["value"]).toDate();
foundType = 'date';
} else { // Must be a time-only result with no date
momentTime = new Date();
momentTime.setHours(parseInt(String(resolutionValues['value']).substr(0, 2)));
momentTime.setMinutes(parseInt(String(resolutionValues['value']).substr(3, 2)));
foundType = 'time';
}
curDateTimeText = {
text: new RegExp(`\\b${result.text}\\b`, "gi"),
dateTimeObj: momentTime,
type: foundType,
range: false
}
}
foundDates.push(curDateTimeText);
});
return foundDates;
}
private formatDate(date: Date, toLocale: string): string {
return this.mapLocaleToFunction[toLocale].dateFormat
.replace('yyyy', (date.getFullYear()).toLocaleString(undefined, {minimumIntegerDigits: 4}).replace(',', ''))
.replace('MM', (date.getMonth() + 1).toLocaleString(undefined, {minimumIntegerDigits: 2}))
.replace('dd', (date.getDate()).toLocaleString(undefined, {minimumIntegerDigits: 2}));
}
private formatTime(date: Date, toLocale: string): string {
return this.mapLocaleToFunction[toLocale].timeFormat
.replace('hh', (date.getHours()).toLocaleString(undefined, {minimumIntegerDigits: 2}))
.replace('mm', (date.getMinutes()).toLocaleString(undefined, {minimumIntegerDigits: 2}));
}
private formatDateAndTime(date: Date, toLocale: string): string {
return `${this.formatDate(date, toLocale)} ${this.formatTime(date, toLocale)}`
}
convert(message: string, fromLocale: string, toLocale: string): Promise<string> {
if (!this.isLocaleAvailable(toLocale)) {
return Promise.reject(`Unsupported to locale ${toLocale}`);
}
try {
let dates: TextAndDateTime[] = this.extractDates(message, fromLocale);
let processedMessage = message;
dates.forEach(date => {
if (date.range) {
if (date.type == 'time') {
let convertedStartDate = this.formatTime(date.dateTimeObj, toLocale);
let convertedEndDate = this.formatTime(date.endDateTimeObj, toLocale);
processedMessage = processedMessage.replace(date.text, `${convertedStartDate} - ${convertedEndDate}`);
} else if (date.type == 'date') {
let convertedStartDate = this.formatDate(date.dateTimeObj, toLocale);
let convertedEndDate = this.formatDate(date.endDateTimeObj, toLocale);
processedMessage = processedMessage.replace(date.text, `${convertedStartDate} - ${convertedEndDate}`);
} else {
let convertedStartDate = this.formatDateAndTime(date.dateTimeObj, toLocale);
let convertedEndDate = this.formatDateAndTime(date.endDateTimeObj, toLocale);
processedMessage = processedMessage.replace(date.text, `${convertedStartDate} - ${convertedEndDate}`);
}
} else {
if (date.type == 'time') {
let convertedDate = this.formatTime(date.dateTimeObj, toLocale);
processedMessage = processedMessage.replace(date.text, convertedDate);
} else if (date.type == 'date') {
let convertedDate = this.formatDate(date.dateTimeObj, toLocale);
processedMessage = processedMessage.replace(date.text, convertedDate);
} else {
let convertedDateTime = this.formatDateAndTime(date.dateTimeObj, toLocale);
processedMessage = processedMessage.replace(date.text, convertedDateTime);
}
}
});
return Promise.resolve(processedMessage);
}
catch(e) {
return Promise.reject(e);
}
}
getAvailableLocales(): Promise<string[]> {
return Promise.resolve(Object.keys(this.mapLocaleToFunction));
}
}
class DateAndTimeLocaleFormat {
public timeFormat: string;
public dateFormat: string;
constructor(timeFormat: string, dateFormat: string) {
this.timeFormat = timeFormat;
this.dateFormat = dateFormat;
}
}
class TextAndDateTime {
public text: RegExp;
public dateTimeObj: Date;
public type: string;
public endDateTimeObj?: Date;
public range: boolean
}