Skip to content

Instantly share code, notes, and snippets.

@stepanselyuk
Created September 1, 2014 13:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stepanselyuk/d76e2d4a162fd1628ffc to your computer and use it in GitHub Desktop.
Save stepanselyuk/d76e2d4a162fd1628ffc to your computer and use it in GitHub Desktop.
Yii::t javascript version
/**
* Created by PhpStorm.
* Author: Stepan Seliuk <stepan@selyuk.com>
* Date: 30/08/14
* Time: 20:20
*/
if ( typeof window.MyApp === 'undefined' ) window.MyApp = {};
MyApp.ttLib = {
defaultCurrency: 'USD',
parseToken: function ( token ) {
var result = token.split( /,\s*/ );
if ( result.length > 3 ) {
// Если элементов больше трех, то пакуем остаток в элемент [2]
// TODO Может быть проблема с пробелами, так как регулярка их съест, и неизвестно были они или нет.
result[2] = result.splice( 2 ).join();
}
var elements = {
// Параметр
'param': result[0],
// Тип обработчика
'type' : result[1]
};
// Если есть правила
if ( typeof result[2] !== 'undefined' ) {
elements.rule = result[2];
}
console.log( 'Parsed token "' + token + '": ' );
console.log( elements );
return elements;
},
processToken: function ( token, paramValue ) {
switch ( token.type ) {
case 'plural':
return MyApp.ttLib.processRulePlural( token, paramValue );
break;
case 'number':
return MyApp.ttLib.processRuleNumber( token, paramValue );
break;
case 'datetime':
return MyApp.ttLib.processRuleDateTime( token, paramValue );
break;
case 'date':
return MyApp.ttLib.processRuleDate( token, paramValue );
break;
case 'time':
return MyApp.ttLib.processRuleTime( token, paramValue );
break;
case 'timespan':
return MyApp.ttLib.processRuleTimespan( token, paramValue );
break;
case 'spellout':
return MyApp.ttLib.processRuleSpellout( token, paramValue );
break;
case 'ordinal':
return MyApp.ttLib.processRuleOrdinal( token, paramValue );
break;
case 'duration':
return MyApp.ttLib.processRuleDuration( token, paramValue );
break;
case 'none':
default :
// Без обработки
return paramValue;
break;
}
},
processRulePlural: function ( token, paramValue ) {
if ( typeof token.rule !== 'undefined' ) {
// Получаем субтокены из правила
// Правило вида: =0{are no cats} =1{is one cat} other{are # cats}
// Поэтому нужно будет еще зафиксировать номера (или значение) перед токеном
var subtokens = MyApp.CommonFunctionsModule.matchRecursiveRegExp( token.rule, '{', '}', 'g' );
var answers = {};
subtokens.forEach( function ( element, index, array ) {
var re = /\b=?(\d+|zero|one|two|few|many|other)\{[^}]+}/gi;
var matches = token.rule.match( re );
console.log( 'Element "' + element + '" has found in the rule: "' + matches[index] + '".' );
if ( typeof matches[index] !== 'undefined' ) {
var sMatch = re.exec( matches[index] );
var ruleset = sMatch[1].trim();
switch ( ruleset ) {
case '0':
ruleset = 'zero';
break;
case '1':
ruleset = 'one';
break;
case '2':
ruleset = 'two';
break;
default:
break;
}
console.log( 'Element "' + element + '" has ruleset "' + ruleset + '".' );
answers[ruleset] = element;
// Замена знака # на переданное значение
if ( /*ruleset == 'other' && */ element.contains( '#' ) ) {
answers[ruleset] = element.replace( '#', paramValue );
}
}
} );
// Доступные правила для языка можно получить через TwitterCldr.PluralRules.all();
// Но по идее правила уже должны быть адаптированы переводчиком для языка
// Теперь применяем простые правила, которые можем рассчитать без дополнительной информации
if ( paramValue == 0 && answers.hasOwnProperty( 'zero' ) ) {
//noinspection JSUnresolvedVariable
return answers.zero;
} else if ( paramValue == 1 && answers.hasOwnProperty( 'one' ) ) {
//noinspection JSUnresolvedVariable
return answers.one;
} else if ( paramValue == 2 && answers.hasOwnProperty( 'two' ) ) {
//noinspection JSUnresolvedVariable
return answers.two;
} else {
// Если есть правило "другие", но нет правил для "few", "many", то возвращаем правило "other"
if ( answers.hasOwnProperty( 'other' ) && !answers.hasOwnProperty( 'few' ) && !answers.hasOwnProperty( 'many' ) ) {
//noinspection JSUnresolvedVariable
return answers.other;
} else {
//noinspection JSUnresolvedVariable
if ( typeof TwitterCldr !== 'undefined' ) {
// Обработка по правилам ICU
//noinspection JSUnresolvedVariable
var ruleset = TwitterCldr.PluralRules.rule_for( paramValue );
console.log( 'The rule for value "' + paramValue + '" is "' + ruleset + '".' );
if ( answers.hasOwnProperty( ruleset ) ) {
// Ответ для правила найден
return answers[ruleset];
} else {
// Не найден ответ для правила
console.log( 'Can\'t find answer body for rule "' + ruleset + '".' );
return paramValue;
}
} else {
// Объект TwitterCldr не найден (нет файла для примененной локали)
console.log( 'Can\'t find Twitter CDLR data object.' );
return paramValue;
}
}
}
} else {
return paramValue;
}
},
processRuleSelect: function ( token, paramValue ) {
console.log( 'Not implemented ' + token + '.' );
return paramValue;
},
processRuleNumber: function ( token, paramValue ) {
//noinspection JSUnresolvedVariable
if ( typeof TwitterCldr !== 'undefined' ) {
var rule = 'integer';
if ( token.hasOwnProperty( 'rule' ) ) {
rule = token.rule;
}
var fmt,
options = {};
switch ( rule ) {
case 'integer':
//noinspection JSUnresolvedVariable
fmt = new TwitterCldr.DecimalFormatter();
return fmt.format( paramValue );
break;
case 'float':
//noinspection JSUnresolvedVariable
fmt = new TwitterCldr.DecimalFormatter();
options.precision = 2;
if ( typeof paramValue === 'object' ) {
options.precision = paramValue.hasOwnProperty( 'precision' ) ? paramValue.precision : 2;
return fmt.format( paramValue.value, options );
} else {
return fmt.format( paramValue, options );
}
break;
case 'shortInteger':
//noinspection JSUnresolvedVariable
fmt = new TwitterCldr.ShortDecimalFormatter();
return fmt.format( paramValue );
break;
case 'longInteger':
//noinspection JSUnresolvedVariable
fmt = new TwitterCldr.LongDecimalFormatter();
return fmt.format( paramValue );
break;
case 'currency':
//noinspection JSUnresolvedVariable
fmt = new TwitterCldr.CurrencyFormatter();
if ( typeof paramValue === 'object' ) {
// заданное значение, или значение по умолчанию, или значение библиотеки по умолчанию
options.currency = paramValue.currency || MyApp.ttData.params.currency || MyApp.ttLib.defaultCurrency;
options.precision = paramValue.hasOwnProperty( 'precision' ) ? paramValue.precision : undefined;
return fmt.format( paramValue.value, options );
} else {
// значение по умолчанию, или значение библиотеки по умолчанию
options.currency = MyApp.ttData.params.currency || MyApp.ttLib.defaultCurrency;
// значение по умолчанию, или значение библиотеки по умолчанию
return fmt.format( paramValue, options );
}
break;
case 'percent':
//noinspection JSUnresolvedVariable
fmt = new TwitterCldr.PercentFormatter();
if ( typeof paramValue === 'object' ) {
options.precision = paramValue.hasOwnProperty( 'precision' ) ? paramValue.precision : 2;
return fmt.format( paramValue.value, options );
} else {
return fmt.format( paramValue );
}
break;
default:
console.log( 'Not implemented ' + token + ' with subtype ' + rule + '.' );
return paramValue;
break;
}
} else {
// Объект TwitterCldr не найден (нет файла для примененной локали)
console.log( 'Can\'t find Twitter CDLR data object.' );
return paramValue;
}
},
processRuleDateTime: function ( token, paramValue, format ) {
format = format || 'date_time';
//noinspection JSUnresolvedVariable
if ( typeof TwitterCldr === 'undefined' ) {
// Объект TwitterCldr не найден (нет файла для примененной локали)
console.log( 'Can\'t find Twitter CDLR data object.' );
return paramValue;
} else {
var rule = 'medium';
if ( token.hasOwnProperty( 'rule' ) ) {
rule = token.rule;
}
//noinspection JSUnresolvedVariable
var fmt = new TwitterCldr.DateTimeFormatter(),
options = {'format': format};
switch ( rule ) {
case 'short':
case 'medium':
case 'long':
case 'full':
options.type = rule;
return fmt.format( new Date( paramValue ), options );
break;
default:
// Другие доступные форматы для локали
//noinspection JSUnresolvedVariable
var otherFormats = TwitterCldr.DateTimeFormatter.additional_formats();
// Проверяем доступность полученного значения
if ( otherFormats.indexOf( rule ) > -1 ) {
options.format = 'additional';
options.type = rule;
// Если найдено, то используем его
return fmt.format( new Date( paramValue ), options );
} else {
console.log( 'Not implemented ' + token + ' with subtype ' + rule + '.' );
return paramValue;
}
break;
}
}
},
processRuleDate: function ( token, paramValue ) {
return MyApp.ttLib.processRuleDateTime( token, paramValue, 'date' );
},
processRuleTime: function ( token, paramValue ) {
return MyApp.ttLib.processRuleDateTime( token, paramValue, 'time' );
},
processRuleTimespan: function ( token, paramValue ) {
//noinspection JSUnresolvedVariable
if ( typeof TwitterCldr === 'undefined' ) {
// Объект TwitterCldr не найден (нет файла для примененной локали)
console.log( 'Can\'t find Twitter CDLR data object.' );
return paramValue;
} else {
var rule = null;
if ( token.hasOwnProperty( 'rule' ) ) {
rule = token.rule;
}
//noinspection JSUnresolvedVariable
var fmt = new TwitterCldr.TimespanFormatter(),
now = Math.round( Date.now() / 1000 ),
then = now,
options = {};
if ( rule !== null ) {
options.unit = rule;
}
if ( typeof paramValue === 'object' ) {
for ( var prop in paramValue ) {
if ( paramValue.hasOwnProperty( prop ) && prop != 'value' ) {
options[prop] = paramValue[prop];
}
}
then = Math.round( new Date( paramValue.value ).getTime() / 1000 );
} else {
then = Math.round( new Date( paramValue ).getTime() / 1000 );
}
return fmt.format( then - now, options );
}
},
processRuleSpellout: function ( token, paramValue ) {
console.log( 'Not implemented ' + token + '.' );
return paramValue;
//noinspection JSUnresolvedVariable
/*if ( typeof TwitterCldr !== 'undefined' ) {
} else {
// Объект TwitterCldr не найден (нет файла для примененной локали)
console.log( 'Can\'t find Twitter CDLR data object.' );
return paramValue;
}*/
},
processRuleOrdinal: function ( token, paramValue ) {
console.log( 'Not implemented ' + token + '.' );
return paramValue;
//noinspection JSUnresolvedVariable
/*if ( typeof TwitterCldr !== 'undefined' ) {
} else {
// Объект TwitterCldr не найден (нет файла для примененной локали)
console.log( 'Can\'t find Twitter CDLR data object.' );
return paramValue;
}*/
},
processRuleDuration: function ( token, paramValue ) {
console.log( 'Not implemented ' + token + '.' );
return paramValue;
//noinspection JSUnresolvedVariable
/*if ( typeof TwitterCldr !== 'undefined' ) {
} else {
// Объект TwitterCldr не найден (нет файла для примененной локали)
console.log( 'Can\'t find Twitter CDLR data object.' );
return paramValue;
}*/
},
processMessage: function ( message, params ) {
if ( MyApp.CommonFunctionsModule.isEmptyObject( params ) ) {
// can't replace templates without data
return message;
}
// Регулярка для определения наличия шаблонов ICU в сообщении
var re = /{\s*[\d\w]+\s*,/;
if ( re.test( message ) ) {
// Содержит логику ICU
var tokens = MyApp.CommonFunctionsModule.matchRecursiveRegExp( message, '{', '}', 'g' );
tokens.forEach( function ( element, index, array ) {
// Парсим токен на компоненты
var token = MyApp.ttLib.parseToken( element );
var paramValue = null;
// Если среди переданных параметров есть параметр, использованный в токене
if ( typeof params[token.param] !== 'undefined' ) {
paramValue = params[token.param];
} else {
// Если нет, и название параметра в токене равно "n", и
// переданные параметры являются массивом, и в них есть нулевой элемент
if ( token.param == 'n' && Array.isArray( params ) && params.length > 0 ) {
paramValue = params[0];
}
}
// Если среди переданных параметров есть параметр, использованный в токене
if ( paramValue !== null ) {
// Процессим токен с переданными параметрами
var result = MyApp.ttLib.processToken( token, paramValue );
if ( typeof result == 'object' && result.hasOwnProperty( 'value' ) ) {
// Если результат в виде объекта, пытаемся выделить значение из него.
result = result.value;
}
// Теперь заменяем найденный токен в сообщении на результат,
// который вернул процессинг токена, если результат не false
console.log( 'element: ' + element );
console.log( 'result: ' + result );
if ( result !== false ) {
message = message.replace( '{' + element + '}', result );
}
}
} );
return message;
} else {
// Обычное сообщение с шаблоном или без
if ( typeof params == 'object' ) {
if ( Array.isArray( params ) ) {
params.forEach( function ( element, index, array ) {
// Заменяем шаблоны в сообщении
message.replace( '{' + index + '}', element );
} );
} else {
for ( var elem in params ) {
if ( params.hasOwnProperty( elem ) ) {
// Заменяем шаблоны в сообщении
message = message.replace( '{' + elem + '}', params[elem] );
}
}
}
} else {
console.log( 'Expected params as "object", received "' + (typeof params) + '".' );
}
}
return message;
}
};
MyApp.tt = function ( message, params ) {
params = params || [];
var lang = $( 'html' ).attr( 'lang' );
if ( typeof MyApp.ttData !== 'undefined' ) {
if ( lang != MyApp.ttData.params.source_lang && message in MyApp.ttData.messages ) {
var translation = MyApp.ttData.messages[message];
console.log( 'Found translation "' + message + '" => "' + translation + '".' );
return MyApp.ttLib.processMessage( translation, params );
} else {
if ( lang == MyApp.ttData.params.source_lang ) {
console.log( 'Translation for "' + message + '" don\'t need as it already in the source language.' );
} else {
console.log( 'Translation for "' + message + '" not found.' );
}
return MyApp.ttLib.processMessage( message, params );
}
} else {
console.log( 'Can\'t found MyApp.ttData...' );
return MyApp.ttLib.processMessage( message, params );
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment