import {IICUMessage, IICUMessageCategory, IICUMessageTranslation, INormalizedMessage} from '../api/index'; import {IMessageParser} from './i-message-parser'; import {isNullOrUndefined, isString} from '../util/tools'; class MessageCategory implements IICUMessageCategory { constructor(private _category: string, private _message: INormalizedMessage) {} public getCategory(): string { return this._category; } public getMessageNormalized(): INormalizedMessage { return this._message; } } /** * Implementation of an ICU Message. * Created by martin on 05.06.2017. */ export class ICUMessage implements IICUMessage { private _isPluralMessage: boolean; private _categories: IICUMessageCategory[]; constructor(private _parser: IMessageParser, isPluralMessage: boolean) { this._isPluralMessage = isPluralMessage; this._categories = []; } addCategory(category: string, message: INormalizedMessage) { this._categories.push(new MessageCategory(category, message)); } /** * ICU message as native string. * This is, how it is stored, something like '{x, plural, =0 {..}' * @return ICU message as native string. */ public asNativeString(): string { const varname = (this.isPluralMessage()) ? 'VAR_PLURAL' : 'VAR_SELECT'; const type = (this.isPluralMessage()) ? 'plural' : 'select'; let choiceString = ''; this._categories.forEach((category: IICUMessageCategory) => { choiceString = choiceString + ` ${category.getCategory()} {${category.getMessageNormalized().asNativeString()}}`; }); return `{${varname}, ${type},${choiceString}`; } /** * Is it a plural message? */ isPluralMessage(): boolean { return this._isPluralMessage; } /** * Is it a select message? */ isSelectMessage(): boolean { return !this._isPluralMessage; } /** * All the parts of the message. * E.g. the ICU message {wolves, plural, =0 {no wolves} =1 {one wolf} =2 {two wolves} other {a wolf pack}} * has 4 category objects with the categories =0, =1, =2, other. */ getCategories(): IICUMessageCategory[] { return this._categories; } /** * Translate message and return a new, translated message * @param translation the translation (hashmap of categories and translations). * @return new message wit translated content. * @throws an error if translation does not match the message. * This is the case, if there are categories not contained in the original message. */ translate(translation: IICUMessageTranslation): IICUMessage { const message = new ICUMessage(this._parser, this.isPluralMessage()); const translatedCategories: Set = new Set(); this._categories.forEach((category) => { let translatedMessage: INormalizedMessage; const translationForCategory: string|IICUMessageTranslation = translation[category.getCategory()]; if (isNullOrUndefined(translationForCategory)) { translatedMessage = category.getMessageNormalized(); } else if (isString(translationForCategory)) { translatedCategories.add(category.getCategory()); translatedMessage = this._parser.parseNormalizedString( translationForCategory, null); } else { // TODO embedded ICU Message translatedMessage = null; } message.addCategory(category.getCategory(), translatedMessage); }); // new categories, which are not part of the original message Object.keys(translation).forEach((categoryName) => { if (!translatedCategories.has(categoryName)) { if (this.isSelectMessage()) { throw new Error(`adding a new category not allowed for select messages ("${categoryName}" is not part of message)`); } else { this.checkValidPluralCategory(categoryName); // TODO embedded ICU Message const translatedMessage = this._parser.parseNormalizedString( translation[categoryName], null); message.addCategory(categoryName, translatedMessage); } } }); return message; } /** * Check, wether category is valid plural category. * Allowed are =n, 'zero', 'one', 'two', 'few', 'many' and 'other' * @param categoryName category * @throws an error, if it is not a valid category name */ private checkValidPluralCategory(categoryName: string) { const allowedKeywords = ['zero', 'one', 'two', 'few', 'many', 'other']; if (categoryName.match(/=\d+/)) { return; } if (allowedKeywords.find((key) => key === categoryName)) { return; } throw new Error(`invalid plural category "${categoryName}", allowed are = and ${allowedKeywords}`); } }