diff --git a/dist/aurelia-i18n.d.ts b/dist/aurelia-i18n.d.ts
index 986322d6..57018dd4 100644
--- a/dist/aurelia-i18n.d.ts
+++ b/dist/aurelia-i18n.d.ts
@@ -1,4 +1,4 @@
-import * as i18next from 'i18next';
+import i18next from 'i18next';
import { Container } from 'aurelia-dependency-injection';
import { EventAggregator } from 'aurelia-event-aggregator';
import { FrameworkConfiguration } from 'aurelia-framework';
diff --git a/dist/umd/aurelia-i18n.js b/dist/umd/aurelia-i18n.js
new file mode 100644
index 00000000..c05abab1
--- /dev/null
+++ b/dist/umd/aurelia-i18n.js
@@ -0,0 +1,1325 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('i18next'), require('aurelia-logging'), require('aurelia-dependency-injection'), require('aurelia-templating'), require('aurelia-metadata'), require('aurelia-pal'), require('aurelia-framework'), require('aurelia-templating-resources'), require('aurelia-event-aggregator'), require('aurelia-binding')) :
+ typeof define === 'function' && define.amd ? define(['exports', 'i18next', 'aurelia-logging', 'aurelia-dependency-injection', 'aurelia-templating', 'aurelia-metadata', 'aurelia-pal', 'aurelia-framework', 'aurelia-templating-resources', 'aurelia-event-aggregator', 'aurelia-binding'], factory) :
+ (factory((global.au = global.au || {}, global.au.i18n = {}),global.i18next,global.au.LogManager,global.au,global.au,global.au,global.au,global.au,global.au,global.au,global.au));
+}(this, (function (exports,i18next,LogManager,aureliaDependencyInjection,aureliaTemplating,aureliaMetadata,aureliaPal,aureliaFramework,aureliaTemplatingResources,aureliaEventAggregator,aureliaBinding) { 'use strict';
+
+ i18next = i18next && i18next.hasOwnProperty('default') ? i18next['default'] : i18next;
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation. All rights reserved.
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+ this file except in compliance with the License. You may obtain a copy of the
+ License at http://www.apache.org/licenses/LICENSE-2.0
+
+ THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+ WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+ MERCHANTABLITY OR NON-INFRINGEMENT.
+
+ See the Apache Version 2.0 License for specific language governing permissions
+ and limitations under the License.
+ ***************************************************************************** */
+
+ function __decorate(decorators, target, key, desc) {
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
+ }
+
+ function __awaiter(thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+ }
+
+ const I18N_EA_SIGNAL = "i18n:locale:changed";
+ class I18N {
+ constructor(ea, signaler) {
+ this.ea = ea;
+ this.signaler = signaler;
+ this.globalVars = {};
+ this.i18next = i18next;
+ this.Intl = aureliaPal.PLATFORM.global.Intl;
+ }
+ static inject() { return [aureliaEventAggregator.EventAggregator, aureliaTemplatingResources.BindingSignaler]; }
+ setup(options) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const defaultOptions = {
+ skipTranslationOnMissingKey: false,
+ compatibilityAPI: "v1",
+ compatibilityJSON: "v1",
+ lng: "en",
+ attributes: ["t", "i18n"],
+ fallbackLng: "en",
+ debug: false
+ };
+ this.i18nextDeferred = new Promise((resolve, reject) => {
+ this.i18next = this.i18next.init(options || defaultOptions, (err) => {
+ if (err) {
+ reject(err);
+ }
+ // make sure attributes is an array in case a string was provided
+ if (this.i18next.options.attributes instanceof String) {
+ this.i18next.options.attributes = [this.i18next.options.attributes];
+ }
+ resolve(this.i18next);
+ });
+ });
+ return this.i18nextDeferred;
+ });
+ }
+ i18nextReady() {
+ return this.i18nextDeferred;
+ }
+ setLocale(locale) {
+ return new Promise((resolve, reject) => {
+ const oldLocale = this.getLocale();
+ this.i18next.changeLanguage(locale, (err, tr) => {
+ if (err) {
+ reject(err);
+ }
+ this.ea.publish(I18N_EA_SIGNAL, { oldValue: oldLocale, newValue: locale });
+ this.signaler.signal("aurelia-translation-signal");
+ resolve(tr);
+ });
+ });
+ }
+ getLocale() {
+ return this.i18next.language;
+ }
+ nf(options, locales) {
+ return new this.Intl.NumberFormat(locales || this.getLocale(), options || {});
+ }
+ uf(numberLike, locale) {
+ const nf = this.nf({}, locale || this.getLocale());
+ const comparer = nf.format(10000 / 3);
+ let thousandSeparator = comparer[1];
+ const decimalSeparator = comparer[5];
+ if (thousandSeparator === ".") {
+ thousandSeparator = "\\.";
+ }
+ // remove all thousand seperators
+ const result = numberLike.replace(new RegExp(thousandSeparator, "g"), "")
+ // remove non-numeric signs except -> , .
+ .replace(/[^\d.,-]/g, "")
+ // replace original decimalSeparator with english one
+ .replace(decimalSeparator, ".");
+ // return real number
+ return Number(result);
+ }
+ df(options, locales) {
+ return new this.Intl.DateTimeFormat(locales || this.getLocale(), options);
+ }
+ tr(key, options) {
+ let fullOptions = this.globalVars;
+ if (options !== undefined) {
+ fullOptions = Object.assign(Object.assign({}, this.globalVars), options);
+ }
+ return this.i18next.t(key, fullOptions);
+ }
+ registerGlobalVariable(key, value) {
+ this.globalVars[key] = value;
+ }
+ unregisterGlobalVariable(key) {
+ delete this.globalVars[key];
+ }
+ /**
+ * Scans an element for children that have a translation attribute and
+ * updates their innerHTML with the current translation values.
+ *
+ * If an image is encountered the translated value will be applied to the src attribute.
+ *
+ * @param el HTMLElement to search within
+ */
+ updateTranslations(el) {
+ if (!el || !el.querySelectorAll) {
+ return;
+ }
+ let i;
+ let l;
+ // create a selector from the specified attributes to look for
+ // var selector = [].concat(this.i18next.options.attributes);
+ const attributes = this.i18next.options.attributes;
+ let selector = [].concat(attributes);
+ for (i = 0, l = selector.length; i < l; i++) {
+ selector[i] = "[" + selector[i] + "]";
+ }
+ selector = selector.join(",");
+ // get the nodes
+ const nodes = el.querySelectorAll(selector);
+ for (i = 0, l = nodes.length; i < l; i++) {
+ const node = nodes[i];
+ let keys;
+ let params;
+ // test every attribute and get the first one that has a value
+ for (let i2 = 0, l2 = attributes.length; i2 < l2; i2++) {
+ keys = node.getAttribute(attributes[i2]);
+ const pname = attributes[i2] + "-params";
+ if (pname && node.au && node.au[pname]) {
+ params = node.au[pname].viewModel.value;
+ }
+ if (keys) {
+ break;
+ }
+ }
+ // skip if nothing was found
+ if (!keys) {
+ continue;
+ }
+ // split the keys into multiple keys separated by a ;
+ this.updateValue(node, keys, params);
+ }
+ }
+ updateValue(node, value, params) {
+ if (value === null || value === undefined) {
+ return;
+ }
+ const keys = value.toString().split(";");
+ let i = keys.length;
+ while (i--) {
+ let key = keys[i];
+ // remove the optional attribute
+ const re = /\[([a-z\-, ]*)\]/ig;
+ let m;
+ let attr = "text";
+ // set default attribute to src if this is an image node
+ if (node.nodeName === "IMG") {
+ attr = "src";
+ }
+ // check if a attribute was specified in the key
+ // tslint:disable-next-line:no-conditional-assignment
+ while ((m = re.exec(key)) !== null) {
+ if (m.index === re.lastIndex) {
+ re.lastIndex++;
+ }
+ if (m) {
+ key = key.replace(m[0], "");
+ attr = m[1];
+ }
+ }
+ const attrs = attr.split(",");
+ let j = attrs.length;
+ while (j--) {
+ attr = attrs[j].trim();
+ if (!node._textContent) {
+ node._textContent = node.textContent;
+ }
+ if (!node._innerHTML) {
+ node._innerHTML = node.innerHTML;
+ }
+ // convert to camelCase
+ // tslint:disable-next-line:only-arrow-functions
+ const attrCC = attr.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
+ const reservedNames = ["prepend", "append", "text", "html"];
+ const i18nLogger = LogManager.getLogger("i18n");
+ if (reservedNames.indexOf(attr) > -1 &&
+ node.au &&
+ node.au.controller &&
+ node.au.controller.viewModel &&
+ attrCC in node.au.controller.viewModel) {
+ i18nLogger.warn(`Aurelia I18N reserved attribute name\n
+ [${reservedNames.join(", ")}]\n
+ Your custom element has a bindable named ${attr} which is a reserved word.\n
+ If you'd like Aurelia I18N to translate your bindable instead, please consider giving it another name.`);
+ }
+ if (this.i18next.options.skipTranslationOnMissingKey &&
+ this.tr(key, params) === key) {
+ i18nLogger.warn(`Couldn't find translation for key: ${key}`);
+ return;
+ }
+ // handle various attributes
+ // anything other than text,prepend,append or html will be added as an attribute on the element.
+ switch (attr) {
+ case "text":
+ const newChild = aureliaPal.DOM.createTextNode(this.tr(key, params));
+ if (node._newChild && node._newChild.parentNode === node) {
+ node.removeChild(node._newChild);
+ }
+ node._newChild = newChild;
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ node.appendChild(node._newChild);
+ break;
+ case "prepend":
+ const prependParser = aureliaPal.DOM.createElement("div");
+ prependParser.innerHTML = this.tr(key, params);
+ for (let ni = node.childNodes.length - 1; ni >= 0; ni--) {
+ if (node.childNodes[ni]._prepended) {
+ node.removeChild(node.childNodes[ni]);
+ }
+ }
+ for (let pi = prependParser.childNodes.length - 1; pi >= 0; pi--) {
+ prependParser.childNodes[pi]._prepended = true;
+ if (node.firstChild) {
+ node.insertBefore(prependParser.childNodes[pi], node.firstChild);
+ }
+ else {
+ node.appendChild(prependParser.childNodes[pi]);
+ }
+ }
+ break;
+ case "append":
+ const appendParser = aureliaPal.DOM.createElement("div");
+ appendParser.innerHTML = this.tr(key, params);
+ for (let ni = node.childNodes.length - 1; ni >= 0; ni--) {
+ if (node.childNodes[ni]._appended) {
+ node.removeChild(node.childNodes[ni]);
+ }
+ }
+ while (appendParser.firstChild) {
+ appendParser.firstChild._appended = true;
+ node.appendChild(appendParser.firstChild);
+ }
+ break;
+ case "html":
+ node.innerHTML = this.tr(key, params);
+ break;
+ default: // normal html attribute
+ if (node.au &&
+ node.au.controller &&
+ node.au.controller.viewModel &&
+ attrCC in node.au.controller.viewModel) {
+ node.au.controller.viewModel[attrCC] = this.tr(key, params);
+ }
+ else {
+ node.setAttribute(attr, this.tr(key, params));
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ exports.TBindingBehavior = class TBindingBehavior {
+ constructor(signalBindingBehavior) {
+ this.signalBindingBehavior = signalBindingBehavior;
+ }
+ static inject() { return [aureliaTemplatingResources.SignalBindingBehavior]; }
+ bind(binding, source) {
+ // bind the signal behavior
+ this.signalBindingBehavior.bind(binding, source, "aurelia-translation-signal");
+ // rewrite the expression to use the TValueConverter.
+ // pass through any args to the binding behavior to the TValueConverter
+ const sourceExpression = binding.sourceExpression;
+ // do create the sourceExpression only once
+ if (sourceExpression.rewritten) {
+ return;
+ }
+ sourceExpression.rewritten = true;
+ const expression = sourceExpression.expression;
+ sourceExpression.expression = new aureliaBinding.ValueConverter(expression, "t", sourceExpression.args, [expression, ...sourceExpression.args]);
+ }
+ unbind(binding, source) {
+ // unbind the signal behavior
+ this.signalBindingBehavior.unbind(binding, source);
+ }
+ };
+ exports.TBindingBehavior = __decorate([
+ aureliaBinding.bindingBehavior("t")
+ ], exports.TBindingBehavior);
+
+ var LazyOptional_1;
+ // tslint:disable-next-line:only-arrow-functions
+ const isInteger = Number.isInteger || function (value) {
+ return typeof value === "number" &&
+ isFinite(value) &&
+ Math.floor(value) === value;
+ };
+ let LazyOptional = LazyOptional_1 = class LazyOptional {
+ constructor(key) {
+ this.key = key;
+ }
+ static of(key) {
+ return new LazyOptional_1(key);
+ }
+ get(container) {
+ return () => {
+ if (container.hasResolver(this.key, false)) {
+ return container.get(this.key);
+ }
+ return null;
+ };
+ }
+ };
+ LazyOptional = LazyOptional_1 = __decorate([
+ aureliaDependencyInjection.resolver()
+ ], LazyOptional);
+
+ var TParamsCustomAttribute_1;
+ exports.TParamsCustomAttribute = TParamsCustomAttribute_1 = class TParamsCustomAttribute {
+ constructor(element) {
+ this.element = element;
+ }
+ static inject() {
+ return [aureliaPal.DOM.Element];
+ }
+ static configureAliases(aliases) {
+ const r = aureliaMetadata.metadata.getOrCreateOwn(aureliaMetadata.metadata.resource, aureliaTemplating.HtmlBehaviorResource, TParamsCustomAttribute_1);
+ r.aliases = aliases;
+ }
+ valueChanged() { }
+ };
+ exports.TParamsCustomAttribute = TParamsCustomAttribute_1 = __decorate([
+ aureliaTemplating.customAttribute("t-params")
+ ], exports.TParamsCustomAttribute);
+
+ var TCustomAttribute_1;
+ exports.TCustomAttribute = TCustomAttribute_1 = class TCustomAttribute {
+ constructor(element, service, ea, p) {
+ this.element = element;
+ this.service = service;
+ this.ea = ea;
+ this.lazyParams = p;
+ }
+ static inject() {
+ return [aureliaPal.DOM.Element, I18N, aureliaEventAggregator.EventAggregator, LazyOptional.of(exports.TParamsCustomAttribute)];
+ }
+ static configureAliases(aliases) {
+ const r = aureliaMetadata.metadata.getOrCreateOwn(aureliaMetadata.metadata.resource, aureliaTemplating.HtmlBehaviorResource, TCustomAttribute_1);
+ r.aliases = aliases;
+ }
+ bind() {
+ this.params = this.lazyParams();
+ if (this.params) {
+ this.params.valueChanged = (newParams, oldParams) => {
+ this.paramsChanged(this.value, newParams, oldParams);
+ };
+ }
+ const p = this.params !== null ? this.params.value : undefined;
+ this.subscription = this.ea.subscribe(I18N_EA_SIGNAL, () => {
+ this.service.updateValue(this.element, this.value, this.params !== null ? this.params.value : undefined);
+ });
+ this.service.updateValue(this.element, this.value, p);
+ }
+ paramsChanged(newValue, newParams) {
+ this.service.updateValue(this.element, newValue, newParams);
+ }
+ valueChanged(newValue) {
+ const p = this.params !== null ? this.params.value : undefined;
+ this.service.updateValue(this.element, newValue, p);
+ }
+ unbind() {
+ // If unbind is called before timeout for subscription is triggered, subscription will be undefined
+ if (this.subscription) {
+ this.subscription.dispose();
+ }
+ }
+ };
+ exports.TCustomAttribute = TCustomAttribute_1 = __decorate([
+ aureliaTemplating.customAttribute("t")
+ ], exports.TCustomAttribute);
+
+ exports.TValueConverter = class TValueConverter {
+ constructor(service) {
+ this.service = service;
+ }
+ static inject() { return [I18N]; }
+ toView(value, options) {
+ return this.service.tr(value, options);
+ }
+ };
+ exports.TValueConverter = __decorate([
+ aureliaFramework.valueConverter("t")
+ ], exports.TValueConverter);
+
+ exports.NfBindingBehavior = class NfBindingBehavior {
+ constructor(signalBindingBehavior) {
+ this.signalBindingBehavior = signalBindingBehavior;
+ }
+ static inject() { return [aureliaTemplatingResources.SignalBindingBehavior]; }
+ bind(binding, source) {
+ // bind the signal behavior
+ this.signalBindingBehavior.bind(binding, source, "aurelia-translation-signal");
+ // rewrite the expression to use the NfValueConverter.
+ // pass through any args to the binding behavior to the NfValueConverter
+ const sourceExpression = binding.sourceExpression;
+ // do create the sourceExpression only once
+ if (sourceExpression.rewritten) {
+ return;
+ }
+ sourceExpression.rewritten = true;
+ const expression = sourceExpression.expression;
+ sourceExpression.expression = new aureliaBinding.ValueConverter(expression, "nf", sourceExpression.args, [expression, ...sourceExpression.args]);
+ }
+ unbind(binding, source) {
+ // unbind the signal behavior
+ this.signalBindingBehavior.unbind(binding, source);
+ }
+ };
+ exports.NfBindingBehavior = __decorate([
+ aureliaBinding.bindingBehavior("nf")
+ ], exports.NfBindingBehavior);
+
+ exports.NfValueConverter = class NfValueConverter {
+ constructor(service) {
+ this.service = service;
+ }
+ static inject() { return [I18N]; }
+ toView(value, nfOrOptions, locale) {
+ if (value === null
+ || typeof value === "undefined"
+ || (typeof value === "string" && value.trim() === "")) {
+ return value;
+ }
+ if (nfOrOptions && (nfOrOptions instanceof Intl.NumberFormat && typeof nfOrOptions.format === "function")) {
+ return nfOrOptions.format(value);
+ }
+ const nf = this.service.nf(nfOrOptions, locale || this.service.getLocale());
+ return nf.format(value);
+ }
+ };
+ exports.NfValueConverter = __decorate([
+ aureliaBinding.valueConverter("nf")
+ ], exports.NfValueConverter);
+
+ exports.DfBindingBehavior = class DfBindingBehavior {
+ constructor(signalBindingBehavior) {
+ this.signalBindingBehavior = signalBindingBehavior;
+ }
+ static inject() { return [aureliaTemplatingResources.SignalBindingBehavior]; }
+ bind(binding, source) {
+ // bind the signal behavior
+ this.signalBindingBehavior.bind(binding, source, "aurelia-translation-signal");
+ // rewrite the expression to use the DfValueConverter.
+ // pass through any args to the binding behavior to the DfValueConverter
+ const sourceExpression = binding.sourceExpression;
+ // do create the sourceExpression only once
+ if (sourceExpression.rewritten) {
+ return;
+ }
+ sourceExpression.rewritten = true;
+ const expression = sourceExpression.expression;
+ sourceExpression.expression = new aureliaBinding.ValueConverter(expression, "df", sourceExpression.args, [expression, ...sourceExpression.args]);
+ }
+ unbind(binding, source) {
+ // unbind the signal behavior
+ this.signalBindingBehavior.unbind(binding, source);
+ }
+ };
+ exports.DfBindingBehavior = __decorate([
+ aureliaBinding.bindingBehavior("df")
+ ], exports.DfBindingBehavior);
+
+ exports.DfValueConverter = class DfValueConverter {
+ constructor(service) {
+ this.service = service;
+ }
+ static inject() { return [I18N]; }
+ toView(value, dfOrOptions, locale) {
+ if (value === null
+ || typeof value === "undefined"
+ || (typeof value === "string" && value.trim() === "")) {
+ return value;
+ }
+ if (typeof value === "string" && isNaN(value) && !isInteger(value)) {
+ value = new Date(value);
+ }
+ if (dfOrOptions && (dfOrOptions instanceof Intl.DateTimeFormat && typeof dfOrOptions.format === "function")) {
+ return dfOrOptions.format(value);
+ }
+ const df = this.service.df(dfOrOptions, locale || this.service.getLocale());
+ return df.format(value);
+ }
+ };
+ exports.DfValueConverter = __decorate([
+ aureliaBinding.valueConverter("df")
+ ], exports.DfValueConverter);
+
+ exports.RtBindingBehavior = class RtBindingBehavior {
+ constructor(signalBindingBehavior) {
+ this.signalBindingBehavior = signalBindingBehavior;
+ }
+ static inject() { return [aureliaTemplatingResources.SignalBindingBehavior]; }
+ bind(binding, source) {
+ // bind the signal behavior
+ this.signalBindingBehavior.bind(binding, source, "aurelia-translation-signal", "aurelia-relativetime-signal");
+ // rewrite the expression to use the RtValueConverter.
+ // pass through any args to the binding behavior to the RtValueConverter
+ const sourceExpression = binding.sourceExpression;
+ // do create the sourceExpression only once
+ if (sourceExpression.rewritten) {
+ return;
+ }
+ sourceExpression.rewritten = true;
+ const expression = sourceExpression.expression;
+ sourceExpression.expression = new aureliaBinding.ValueConverter(expression, "rt", sourceExpression.args, [expression, ...sourceExpression.args]);
+ }
+ unbind(binding, source) {
+ // unbind the signal behavior
+ this.signalBindingBehavior.unbind(binding, source);
+ }
+ };
+ exports.RtBindingBehavior = __decorate([
+ aureliaBinding.bindingBehavior("rt")
+ ], exports.RtBindingBehavior);
+
+ const translations = {
+ ar: {
+ translation: {
+ now: 'الآن',
+ second_ago: 'منذ __count__ ثانية',
+ second_ago_plural: 'منذ __count__ ثواني',
+ second_in: 'في __count__ ثانية',
+ second_in_plural: 'في __count__ ثواني',
+ minute_ago: 'منذ __count__ دقيقة',
+ minute_ago_plural: 'منذ __count__ دقائق',
+ minute_in: 'في __count__ دقيقة',
+ minute_in_plural: 'في __count__ دقائق',
+ hour_ago: 'منذ __count__ ساعة',
+ hour_ago_plural: 'منذ __count__ ساعات',
+ hour_in: 'في __count__ ساعة',
+ hour_in_plural: 'في __count__ ساعات',
+ day_ago: 'منذ __count__ يوم',
+ day_ago_plural: 'منذ __count__ أيام',
+ day_in: 'في __count__ يوم',
+ day_in_plural: 'في __count__ أيام',
+ month_ago: 'منذ __count__ شهر',
+ month_ago_plural: 'منذ __count__ أشهر',
+ month_in: 'في __count__ شهر',
+ month_in_plural: 'في __count__ أشهر',
+ year_ago: 'منذ __count__ سنة',
+ year_ago_plural: 'منذ __count__ سنوات',
+ year_in: 'في __count__ سنة',
+ year_in_plural: 'في __count__ سنوات'
+ }
+ },
+ da: {
+ translation: {
+ now: 'lige nu',
+ second_ago: '__count__ sekund siden',
+ second_ago_plural: '__count__ sekunder siden',
+ second_in: 'i __count__ sekund',
+ second_in_plural: 'i __count__ sekunder',
+ minute_ago: '__count__ minut siden',
+ minute_ago_plural: '__count__ minutter siden',
+ minute_in: 'i __count__ minut',
+ minute_in_plural: 'i __count__ minutter',
+ hour_ago: '__count__ time siden',
+ hour_ago_plural: '__count__ timer siden',
+ hour_in: 'i __count__ time',
+ hour_in_plural: 'i __count__ timer',
+ day_ago: '__count__ dag siden',
+ day_ago_plural: '__count__ dage siden',
+ day_in: 'i __count__ dag',
+ day_in_plural: 'i __count__ dage',
+ month_ago: '__count__ måned siden',
+ month_ago_plural: '__count__ måneder siden',
+ month_in: 'i __count__ måned',
+ month_in_plural: 'i __count__ måneder',
+ year_ago: '__count__ år siden',
+ year_ago_plural: '__count__ år siden',
+ year_in: 'i __count__ år',
+ year_in_plural: 'i __count__ år'
+ }
+ },
+ de: {
+ translation: {
+ now: 'jetzt gerade',
+ second_ago: 'vor __count__ Sekunde',
+ second_ago_plural: 'vor __count__ Sekunden',
+ second_in: 'in __count__ Sekunde',
+ second_in_plural: 'in __count__ Sekunden',
+ minute_ago: 'vor __count__ Minute',
+ minute_ago_plural: 'vor __count__ Minuten',
+ minute_in: 'in __count__ Minute',
+ minute_in_plural: 'in __count__ Minuten',
+ hour_ago: 'vor __count__ Stunde',
+ hour_ago_plural: 'vor __count__ Stunden',
+ hour_in: 'in __count__ Stunde',
+ hour_in_plural: 'in __count__ Stunden',
+ day_ago: 'vor __count__ Tag',
+ day_ago_plural: 'vor __count__ Tagen',
+ day_in: 'in __count__ Tag',
+ day_in_plural: 'in __count__ Tagen',
+ month_ago: 'vor __count__ Monat',
+ month_ago_plural: 'vor __count__ Monaten',
+ month_in: 'in __count__ Monat',
+ month_in_plural: 'in __count__ Monaten',
+ year_ago: 'vor __count__ Jahr',
+ year_ago_plural: 'vor __count__ Jahren',
+ year_in: 'in __count__ Jahr',
+ year_in_plural: 'in __count__ Jahren'
+ }
+ },
+ en: {
+ translation: {
+ now: 'just now',
+ second_ago: '__count__ second ago',
+ second_ago_plural: '__count__ seconds ago',
+ second_in: 'in __count__ second',
+ second_in_plural: 'in __count__ seconds',
+ minute_ago: '__count__ minute ago',
+ minute_ago_plural: '__count__ minutes ago',
+ minute_in: 'in __count__ minute',
+ minute_in_plural: 'in __count__ minutes',
+ hour_ago: '__count__ hour ago',
+ hour_ago_plural: '__count__ hours ago',
+ hour_in: 'in __count__ hour',
+ hour_in_plural: 'in __count__ hours',
+ day_ago: '__count__ day ago',
+ day_ago_plural: '__count__ days ago',
+ day_in: 'in __count__ day',
+ day_in_plural: 'in __count__ days',
+ month_ago: '__count__ month ago',
+ month_ago_plural: '__count__ months ago',
+ month_in: 'in __count__ month',
+ month_in_plural: 'in __count__ months',
+ year_ago: '__count__ year ago',
+ year_ago_plural: '__count__ years ago',
+ year_in: 'in __count__ year',
+ year_in_plural: 'in __count__ years'
+ }
+ },
+ es: {
+ translation: {
+ now: 'ahora mismo',
+ second_ago: 'hace __count__ segundo',
+ second_ago_plural: 'hace __count__ segundos',
+ second_in: 'en __count__ segundo',
+ second_in_plural: 'en __count__ segundos',
+ minute_ago: 'hace __count__ minuto',
+ minute_ago_plural: 'hace __count__ minutos',
+ minute_in: 'en __count__ minuto',
+ minute_in_plural: 'en __count__ minutos',
+ hour_ago: 'hace __count__ hora',
+ hour_ago_plural: 'hace __count__ horas',
+ hour_in: 'en __count__ hora',
+ hour_in_plural: 'en __count__ horas',
+ day_ago: 'hace __count__ día',
+ day_ago_plural: 'hace __count__ días',
+ day_in: 'en __count__ día',
+ day_in_plural: 'en __count__ días',
+ month_ago: 'hace __count__ mes',
+ month_ago_plural: 'hace __count__ meses',
+ month_in: 'en __count__ mes',
+ month_in_plural: 'en __count__ meses',
+ year_ago: 'hace __count__ año',
+ year_ago_plural: 'hace __count__ años',
+ year_in: 'en __count__ año',
+ year_in_plural: 'en __count__ años'
+ }
+ },
+ fi: {
+ translation: {
+ now: 'Nyt',
+ second_ago: '__count__ sekuntti sitten',
+ second_ago_plural: '__count__ sekunttia sitten',
+ second_in: ' __count__ sekunnin kuluttua',
+ second_in_plural: ' __count__ sekunttien kuluttua',
+ minute_ago: '__count__ minuutti sitten',
+ minute_ago_plural: '__count__ minuuttia sitten',
+ minute_in: ' __count__ minuutin kuluttua',
+ minute_in_plural: ' __count__ minuuttien kuluttua',
+ hour_ago: '__count__ tunti sitten',
+ hour_ago_plural: '__count__ tuntia sitten',
+ hour_in: ' __count__ tunnin kuluttua',
+ hour_in_plural: ' __count__ tunnin kuluttua',
+ day_ago: '__count__ päivä sitten',
+ day_ago_plural: '__count__ päiviä sitten',
+ day_in: ' __count__ päivän kuluttua',
+ day_in_plural: '__count__ päivien kuluttua',
+ month_ago: '__count__ kuukausi sitten',
+ month_ago_plural: '__count__ kuukausia sitten',
+ month_in: ' __count__ kuukauden kuluttua',
+ month_in_plural: ' __count__ kuukausien kuluttua',
+ year_ago: '__count__ vuosi sitten',
+ year_ago_plural: '__count__ vuosia sitten',
+ year_in: ' __count__ vuoden kuluttua',
+ year_in_plural: ' __count__ vuosien kuluttua'
+ }
+ },
+ fr: {
+ translation: {
+ now: 'maintenant',
+ second_ago: 'il y a __count__ seconde',
+ second_ago_plural: 'il y a __count__ secondes',
+ second_in: 'dans __count__ seconde',
+ second_in_plural: 'dans __count__ secondes',
+ minute_ago: 'il y a __count__ minute',
+ minute_ago_plural: 'il y a __count__ minutes',
+ minute_in: 'dans __count__ minute',
+ minute_in_plural: 'dans __count__ minutes',
+ hour_ago: 'il y a __count__ heure',
+ hour_ago_plural: 'il y a __count__ heures',
+ hour_in: 'dans __count__ heure',
+ hour_in_plural: 'dans __count__ heures',
+ day_ago: 'il y a __count__ jour',
+ day_ago_plural: 'il y a __count__ jours',
+ day_in: 'dans __count__ jour',
+ day_in_plural: 'dans __count__ jours',
+ month_ago: 'il y a __count__ mois',
+ month_ago_plural: 'il y a __count__ mois',
+ month_in: 'dans __count__ mois',
+ month_in_plural: 'dans __count__ mois',
+ year_ago: 'il y a __count__ an',
+ year_ago_plural: 'il y a __count__ ans',
+ year_in: 'dans __count__ an',
+ year_in_plural: 'dans __count__ ans'
+ }
+ },
+ it: {
+ translation: {
+ now: 'adesso',
+ second_ago: '__count__ secondo fa',
+ second_ago_plural: '__count__ secondi fa',
+ second_in: 'in __count__ secondo',
+ second_in_plural: 'in __count__ secondi',
+ minute_ago: '__count__ minuto fa',
+ minute_ago_plural: '__count__ minuti fa',
+ minute_in: 'in __count__ minuto',
+ minute_in_plural: 'in __count__ minuti',
+ hour_ago: '__count__ ora fa',
+ hour_ago_plural: '__count__ ore fa',
+ hour_in: 'in __count__ ora',
+ hour_in_plural: 'in __count__ ore',
+ day_ago: '__count__ giorno fa',
+ day_ago_plural: '__count__ giorni fa',
+ day_in: 'in __count__ giorno',
+ day_in_plural: 'in __count__ giorni',
+ month_ago: '__count__ mese fa',
+ month_ago_plural: '__count__ mesi fa',
+ month_in: 'in __count__ mese',
+ month_in_plural: 'in __count__ mesi',
+ year_ago: '__count__ anno fa',
+ year_ago_plural: '__count__ anni fa',
+ year_in: 'in __count__ anno',
+ year_in_plural: 'in __count__ anni'
+ }
+ },
+ ja: {
+ translation: {
+ now: 'たった今',
+ second_ago: '__count__ 秒前',
+ second_ago_plural: '__count__ 秒前',
+ second_in: 'あと __count__ 秒',
+ second_in_plural: 'あと __count__ 秒',
+ minute_ago: '__count__ 分前',
+ minute_ago_plural: '__count__ 分前',
+ minute_in: 'あと __count__ 分',
+ minute_in_plural: 'あと __count__ 分',
+ hour_ago: '__count__ 時間前',
+ hour_ago_plural: '__count__ 時間前',
+ hour_in: 'あと __count__ 時間',
+ hour_in_plural: 'あと __count__ 時間',
+ day_ago: '__count__ 日間前',
+ day_ago_plural: '__count__ 日間前',
+ day_in: 'あと __count__ 日間',
+ day_in_plural: 'あと __count__ 日間',
+ month_ago: '__count__ ヶ月前',
+ month_ago_plural: '__count__ ヶ月前',
+ month_in: 'あと __count__ ヶ月前',
+ month_in_plural: 'あと __count__ ヶ月前',
+ year_ago: '__count__ 年前',
+ year_ago_plural: '__count__ 年前',
+ year_in: 'あと __count__ 年',
+ year_in_plural: 'あと __count__ 年'
+ }
+ },
+ nl: {
+ translation: {
+ now: 'zonet',
+ second_ago: '__count__ seconde geleden',
+ second_ago_plural: '__count__ seconden geleden',
+ second_in: 'in __count__ seconde',
+ second_in_plural: 'in __count__ seconden',
+ minute_ago: '__count__ minuut geleden',
+ minute_ago_plural: '__count__ minuten geleden',
+ minute_in: 'in __count__ minuut',
+ minute_in_plural: 'in __count__ minuten',
+ hour_ago: '__count__ uur geleden',
+ hour_ago_plural: '__count__ uren geleden',
+ hour_in: 'in __count__ uur',
+ hour_in_plural: 'in __count__ uren',
+ day_ago: '__count__ dag geleden',
+ day_ago_plural: '__count__ dagen geleden',
+ day_in: 'in __count__ dag',
+ day_in_plural: 'in __count__ dagen',
+ month_ago: '__count__ maand geleden',
+ month_ago_plural: '__count__ maanden geleden',
+ month_in: 'in __count__ maand',
+ month_in_plural: 'in __count__ maanden',
+ year_ago: '__count__ jaar geleden',
+ year_ago_plural: '__count__ jaren geleden',
+ year_in: 'in __count__ jaar',
+ year_in_plural: 'in __count__ jaren'
+ }
+ },
+ nn: {
+ translation: {
+ now: 'akkurat nå',
+ second_ago: '__count__ sekund siden',
+ second_ago_plural: '__count__ sekunder siden',
+ second_in: 'om __count__ sekund',
+ second_in_plural: 'om __count__ sekunder',
+ minute_ago: '__count__ minutt siden',
+ minute_ago_plural: '__count__ minutter siden',
+ minute_in: 'om __count__ minutt',
+ minute_in_plural: 'om __count__ minutter',
+ hour_ago: '__count__ time siden',
+ hour_ago_plural: '__count__ timer siden',
+ hour_in: 'om __count__ time',
+ hour_in_plural: 'om __count__ timer',
+ day_ago: '__count__ dag siden',
+ day_ago_plural: '__count__ dager siden',
+ day_in: 'om __count__ dag',
+ day_in_plural: 'om __count__ dager',
+ month_ago: '__count__ en måned siden',
+ month_ago_plural: '__count__ flere måneder siden',
+ month_in: 'I løpet av en __count__ måned',
+ month_in_plural: 'I løpet av __count__ måneder',
+ year_ago: '__count__ et år siden',
+ year_ago_plural: '__count__ flere å siden',
+ year_in: 'I løpet av ett år __count__ år',
+ year_in_plural: 'på flere __count__ år'
+ }
+ },
+ no: {
+ translation: {
+ now: 'akkurat nå',
+ second_ago: '__count__ sekund siden',
+ second_ago_plural: '__count__ sekunder siden',
+ second_in: 'om __count__ sekund',
+ second_in_plural: 'om __count__ sekunder',
+ minute_ago: '__count__ minutt siden',
+ minute_ago_plural: '__count__ minutter siden',
+ minute_in: 'om __count__ minutt',
+ minute_in_plural: 'om __count__ minutter',
+ hour_ago: '__count__ time siden',
+ hour_ago_plural: '__count__ timer siden',
+ hour_in: 'om __count__ time',
+ hour_in_plural: 'om __count__ timer',
+ day_ago: '__count__ dag siden',
+ day_ago_plural: '__count__ dager siden',
+ day_in: 'om __count__ dag',
+ day_in_plural: 'om __count__ dager',
+ month_ago: '__count__ en måned siden',
+ month_ago_plural: '__count__ flere måneder siden',
+ month_in: 'I løpet av en __count__ måned',
+ month_in_plural: 'I løpet av __count__ måneder',
+ year_ago: '__count__ et år siden',
+ year_ago_plural: '__count__ flere å siden',
+ year_in: 'I løpet av ett år __count__ år',
+ year_in_plural: 'på flere __count__ år'
+ }
+ },
+ pt: {
+ translation: {
+ now: 'neste exato momento',
+ second_ago: '__count__ segundo atrás',
+ second_ago_plural: '__count__ segundos atrás',
+ second_in: 'em __count__ segundo',
+ second_in_plural: 'em __count__ segundos',
+ minute_ago: '__count__ minuto atrás',
+ minute_ago_plural: '__count__ minutos atrás',
+ minute_in: 'em __count__ minuto',
+ minute_in_plural: 'em __count__ minutos',
+ hour_ago: '__count__ hora atrás',
+ hour_ago_plural: '__count__ horas atrás',
+ hour_in: 'em __count__ hora',
+ hour_in_plural: 'em __count__ horas',
+ day_ago: '__count__ dia atrás',
+ day_ago_plural: '__count__ dias atrás',
+ day_in: 'em __count__ dia',
+ day_in_plural: 'em __count__ dias',
+ month_ago: '__count__ mês atrás',
+ month_ago_plural: '__count__ meses atrás',
+ month_in: 'em __count__ mês',
+ month_in_plural: 'em __count__ meses',
+ year_ago: '__count__ ano atrás',
+ year_ago_plural: '__count__ anos atrás',
+ year_in: 'em __count__ ano',
+ year_in_plural: 'em __count__ anos'
+ }
+ },
+ sv: {
+ translation: {
+ now: 'nu',
+ second_ago: '__count__ sekund sedan',
+ second_ago_plural: '__count__ sekunder sedan',
+ second_in: 'om __count__ sekund',
+ second_in_plural: 'om __count__ sekunder',
+ minute_ago: '__count__ minut sedan',
+ minute_ago_plural: '__count__ minuter sedan',
+ minute_in: 'om __count__ minut',
+ minute_in_plural: 'om __count__ minuter',
+ hour_ago: '__count__ timme sedan',
+ hour_ago_plural: '__count__ timmar sedan',
+ hour_in: 'om __count__ timme',
+ hour_in_plural: 'om __count__ timmar',
+ day_ago: '__count__ dag sedan',
+ day_ago_plural: '__count__ dagar sedan',
+ day_in: 'om __count__ dag',
+ day_in_plural: 'om __count__ dagar',
+ month_ago: '__count__ månad sedan',
+ month_ago_plural: '__count__ månader sedan',
+ month_in: 'om __count__ månad',
+ month_in_plural: 'om __count__ månader',
+ year_ago: '__count__ år sedan',
+ year_ago_plural: '__count__ år sedan',
+ year_in: 'om __count__ år',
+ year_in_plural: 'om __count__ år'
+ }
+ },
+ th: {
+ translation: {
+ now: 'เมื่อกี้',
+ second_ago: '__count__ วินาที ที่ผ่านมา',
+ second_ago_plural: '__count__ วินาที ที่ผ่านมา',
+ second_in: 'อีก __count__ วินาที',
+ second_in_plural: 'อีก __count__ วินาที',
+ minute_ago: '__count__ นาที ที่ผ่านมา',
+ minute_ago_plural: '__count__ นาที ที่ผ่านมา',
+ minute_in: 'อีก __count__ นาที',
+ minute_in_plural: 'อีก __count__ นาที',
+ hour_ago: '__count__ ชั่วโมง ที่ผ่านมา',
+ hour_ago_plural: '__count__ ชั่วโมง ที่ผ่านมา',
+ hour_in: 'อีก __count__ ชั่วโมง',
+ hour_in_plural: 'อีก __count__ ชั่วโมง',
+ day_ago: '__count__ วัน ที่ผ่านมา',
+ day_ago_plural: '__count__ วัน ที่ผ่านมา',
+ day_in: 'อีก __count__ วัน',
+ day_in_plural: 'อีก __count__ วัน'
+ }
+ },
+ zh: {
+ translation: {
+ now: '刚才',
+ second_ago: '__count__ 秒钟前',
+ second_ago_plural: '__count__ 秒钟前',
+ second_in: '__count__ 秒内',
+ second_in_plural: '__count__ 秒内',
+ minute_ago: '__count__ 分钟前',
+ minute_ago_plural: '__count__ 分钟前',
+ minute_in: '__count__ 分钟内',
+ minute_in_plural: '__count__ 分钟内',
+ hour_ago: '__count__ 小时前',
+ hour_ago_plural: '__count__ 小时前',
+ hour_in: '__count__ 小时内',
+ hour_in_plural: '__count__ 小时内',
+ day_ago: '__count__ 天前',
+ day_ago_plural: '__count__ 天前',
+ day_in: '__count__ 天内',
+ day_in_plural: '__count__ 天内',
+ month_ago: '__count__ 月前',
+ month_ago_plural: '__count__ 月前',
+ month_in: '__count__ 月内',
+ month_in_plural: '__count__ 月内',
+ year_ago: '__count__ 年前',
+ year_ago_plural: '__count__ 年前',
+ year_in: '__count__ 年内',
+ year_in_plural: '__count__ 年内'
+ }
+ },
+ 'zh-CN': {
+ translation: {
+ now: '刚才',
+ second_ago: '__count__ 秒钟前',
+ second_ago_plural: '__count__ 秒钟前',
+ second_in: '__count__ 秒内',
+ second_in_plural: '__count__ 秒内',
+ minute_ago: '__count__ 分钟前',
+ minute_ago_plural: '__count__ 分钟前',
+ minute_in: '__count__ 分钟内',
+ minute_in_plural: '__count__ 分钟内',
+ hour_ago: '__count__ 小时前',
+ hour_ago_plural: '__count__ 小时前',
+ hour_in: '__count__ 小时内',
+ hour_in_plural: '__count__ 小时内',
+ day_ago: '__count__ 天前',
+ day_ago_plural: '__count__ 天前',
+ day_in: '__count__ 天内',
+ day_in_plural: '__count__ 天内',
+ month_ago: '__count__ 月前',
+ month_ago_plural: '__count__ 月前',
+ month_in: '__count__ 月内',
+ month_in_plural: '__count__ 月内',
+ year_ago: '__count__ 年前',
+ year_ago_plural: '__count__ 年前',
+ year_in: '__count__ 年内',
+ year_in_plural: '__count__ 年内'
+ }
+ },
+ 'zh-HK': {
+ translation: {
+ now: '剛才',
+ second_ago: '__count__ 秒鐘前',
+ second_ago_plural: '__count__ 秒鐘前',
+ second_in: '__count__ 秒內',
+ second_in_plural: '__count__ 秒內',
+ minute_ago: '__count__ 分鐘前',
+ minute_ago_plural: '__count__ 分鐘前',
+ minute_in: '__count__ 分鐘內',
+ minute_in_plural: '__count__ 分鐘內',
+ hour_ago: '__count__ 小時前',
+ hour_ago_plural: '__count__ 小時前',
+ hour_in: '__count__ 小時內',
+ hour_in_plural: '__count__ 小時內',
+ day_ago: '__count__ 天前',
+ day_ago_plural: '__count__ 天前',
+ day_in: '__count__ 天內',
+ day_in_plural: '__count__ 天內',
+ month_ago: '__count__ 月前',
+ month_ago_plural: '__count__ 月前',
+ month_in: '__count__ 月內',
+ month_in_plural: '__count__ 月內',
+ year_ago: '__count__ 年前',
+ year_ago_plural: '__count__ 年前',
+ year_in: '__count__ 年內',
+ year_in_plural: '__count__ 年內'
+ }
+ },
+ 'zh-TW': {
+ translation: {
+ now: '剛才',
+ second_ago: '__count__ 秒鐘前',
+ second_ago_plural: '__count__ 秒鐘前',
+ second_in: '__count__ 秒內',
+ second_in_plural: '__count__ 秒內',
+ minute_ago: '__count__ 分鐘前',
+ minute_ago_plural: '__count__ 分鐘前',
+ minute_in: '__count__ 分鐘內',
+ minute_in_plural: '__count__ 分鐘內',
+ hour_ago: '__count__ 小時前',
+ hour_ago_plural: '__count__ 小時前',
+ hour_in: '__count__ 小時內',
+ hour_in_plural: '__count__ 小時內',
+ day_ago: '__count__ 天前',
+ day_ago_plural: '__count__ 天前',
+ day_in: '__count__ 天內',
+ day_in_plural: '__count__ 天內',
+ month_ago: '__count__ 月前',
+ month_ago_plural: '__count__ 月前',
+ month_in: '__count__ 月內',
+ month_in_plural: '__count__ 月內',
+ year_ago: '__count__ 年前',
+ year_ago_plural: '__count__ 年前',
+ year_in: '__count__ 年內',
+ year_in_plural: '__count__ 年內'
+ }
+ }
+ };
+ // tslint:enable
+
+ class RelativeTime {
+ constructor(service, ea) {
+ this.service = service;
+ this.ea = ea;
+ this.service.i18nextReady().then(() => {
+ this.setup();
+ });
+ this.ea.subscribe(I18N_EA_SIGNAL, (locales) => {
+ this.setup(locales);
+ });
+ }
+ static inject() { return [I18N, aureliaEventAggregator.EventAggregator]; }
+ setup(locales) {
+ const trans = translations;
+ const fallbackLng = this.service.i18next.fallbackLng;
+ let alternateFb = fallbackLng || this.service.i18next.options.fallbackLng;
+ if (Array.isArray(alternateFb) && alternateFb.length > 0) {
+ alternateFb = alternateFb[0];
+ }
+ const key = ((locales && locales.newValue)
+ ? locales.newValue
+ : this.service.getLocale()) || alternateFb;
+ let index = 0;
+ // tslint:disable-next-line:no-conditional-assignment
+ if ((index = key.indexOf("-")) >= 0) {
+ const baseLocale = key.substring(0, index);
+ if (trans[baseLocale]) {
+ this.addTranslationResource(baseLocale, trans[baseLocale].translation);
+ }
+ }
+ if (trans[key]) {
+ this.addTranslationResource(key, trans[key].translation);
+ }
+ if (trans[fallbackLng]) {
+ this.addTranslationResource(key, trans[fallbackLng].translation);
+ }
+ }
+ addTranslationResource(key, translation) {
+ const options = this.service.i18next.options;
+ if (options.interpolation && (options.interpolation.prefix !== "__" || options.interpolation.suffix !== "__")) {
+ // tslint:disable-next-line:forin
+ for (const subkey in translation) {
+ translation[subkey] = translation[subkey]
+ .replace("__count__", `${options.interpolation.prefix || "{{"}count${options.interpolation.suffix || "}}"}`);
+ }
+ }
+ this.service.i18next.addResources(key, options.defaultNS || "translation", translation);
+ }
+ getRelativeTime(time) {
+ const now = new Date();
+ const diff = now.getTime() - time.getTime();
+ let timeDiff = this.getTimeDiffDescription(diff, "year", 31104000000);
+ if (!timeDiff) {
+ timeDiff = this.getTimeDiffDescription(diff, "month", 2592000000);
+ if (!timeDiff) {
+ timeDiff = this.getTimeDiffDescription(diff, "day", 86400000);
+ if (!timeDiff) {
+ timeDiff = this.getTimeDiffDescription(diff, "hour", 3600000);
+ if (!timeDiff) {
+ timeDiff = this.getTimeDiffDescription(diff, "minute", 60000);
+ if (!timeDiff) {
+ timeDiff = this.getTimeDiffDescription(diff, "second", 1000);
+ if (!timeDiff) {
+ timeDiff = this.service.tr("now");
+ }
+ }
+ }
+ }
+ }
+ }
+ return timeDiff;
+ }
+ getTimeDiffDescription(diff, unit, timeDivisor) {
+ const unitAmount = parseInt((diff / timeDivisor).toFixed(0), 10);
+ if (unitAmount > 0) {
+ return this.service.tr(unit, { count: unitAmount, context: "ago" });
+ }
+ else if (unitAmount < 0) {
+ const abs = Math.abs(unitAmount);
+ return this.service.tr(unit, { count: abs, context: "in" });
+ }
+ return null;
+ }
+ }
+
+ exports.RtValueConverter = class RtValueConverter {
+ constructor(service) {
+ this.service = service;
+ }
+ static inject() { return [RelativeTime]; }
+ toView(value) {
+ if (value === null
+ || typeof value === "undefined"
+ || (typeof value === "string" && value.trim() === "")) {
+ return value;
+ }
+ if (typeof value === "string" && isNaN(value) && !Number.isInteger(value)) {
+ value = new Date(value);
+ }
+ return this.service.getRelativeTime(value);
+ }
+ };
+ exports.RtValueConverter = __decorate([
+ aureliaBinding.valueConverter("rt")
+ ], exports.RtValueConverter);
+
+ class Backend {
+ constructor(services, options = {}) {
+ this.services = services;
+ this.options = options;
+ this.type = "backend";
+ this.init(services, options);
+ }
+ static with(loader) {
+ this.loader = loader;
+ return this;
+ }
+ init(services, options = {}) {
+ this.services = services;
+ this.options = Object.assign({}, {
+ loadPath: "/locales/{{lng}}/{{ns}}.json",
+ addPath: "locales/add/{{lng}}/{{ns}}",
+ allowMultiLoading: false,
+ parse: JSON.parse
+ }, options);
+ }
+ readMulti(languages, namespaces, callback) {
+ let loadPath = this.options.loadPath;
+ if (typeof this.options.loadPath === "function") {
+ loadPath = this.options.loadPath(languages, namespaces);
+ }
+ const url = this.services
+ .interpolator
+ .interpolate(loadPath, { lng: languages.join("+"), ns: namespaces.join("+") });
+ this.loadUrl(url, callback);
+ }
+ read(language, namespace, callback) {
+ let loadPath = this.options.loadPath;
+ if (typeof this.options.loadPath === "function") {
+ loadPath = this.options.loadPath([language], [namespace]);
+ }
+ const url = this.services.interpolator.interpolate(loadPath, { lng: language, ns: namespace });
+ this.loadUrl(url, callback);
+ }
+ loadUrl(url, callback) {
+ return __awaiter(this, void 0, void 0, function* () {
+ try {
+ const response = yield Backend.loader.loadText(url);
+ let ret;
+ let err;
+ try {
+ ret = (response instanceof Object) ? response : this.options.parse(response, url);
+ }
+ catch (e) {
+ err = "failed parsing " + url + " to json";
+ }
+ if (err) {
+ return callback(err, false);
+ }
+ callback(null, ret);
+ }
+ catch (_a) {
+ callback("failed loading " + url, false /* no retry */);
+ }
+ });
+ }
+ // tslint:disable-next-line:variable-name
+ create(_languages, _namespace, _key, _fallbackValue) {
+ // not supported
+ }
+ }
+ Backend.type = "backend";
+
+ function configure(frameworkConfig, cb) {
+ if (typeof cb !== "function") {
+ const errorMsg = "You need to provide a callback method to properly configure the library";
+ throw errorMsg;
+ }
+ const instance = frameworkConfig.container.get(I18N);
+ const ret = cb(instance);
+ frameworkConfig.globalResources([
+ exports.TValueConverter,
+ exports.TBindingBehavior,
+ exports.TCustomAttribute,
+ exports.TParamsCustomAttribute,
+ exports.NfValueConverter,
+ exports.NfBindingBehavior,
+ exports.DfValueConverter,
+ exports.DfBindingBehavior,
+ exports.RtValueConverter,
+ exports.RtBindingBehavior
+ ]);
+ frameworkConfig.postTask(() => {
+ const resources = frameworkConfig.container.get(aureliaTemplating.ViewResources);
+ const htmlBehaviorResource = resources.getAttribute("t");
+ const htmlParamsResource = resources.getAttribute("t-params");
+ let attributes = instance.i18next.options.attributes;
+ // Register default attributes if none provided
+ if (!attributes) {
+ attributes = ["t", "i18n"];
+ }
+ attributes.forEach((alias) => resources.registerAttribute(alias, htmlBehaviorResource, "t"));
+ attributes.forEach((alias) => resources.registerAttribute(alias + "-params", htmlParamsResource, "t-params"));
+ });
+ return ret;
+ }
+
+ exports.configure = configure;
+ exports.I18N_EA_SIGNAL = I18N_EA_SIGNAL;
+ exports.I18N = I18N;
+ exports.RelativeTime = RelativeTime;
+ exports.Backend = Backend;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md
index 48e74c07..9d131972 100644
--- a/doc/CHANGELOG.md
+++ b/doc/CHANGELOG.md
@@ -1,3 +1,13 @@
+
+# [3.0.0-beta.5](https://github.com/aurelia/i18n/compare/3.0.0-beta.4...3.0.0-beta.5) (2019-01-19)
+
+
+### Bug Fixes
+
+* **typings:** proper import of i18next ([#293](https://github.com/aurelia/i18n/issues/293)) ([13a9f9d](https://github.com/aurelia/i18n/commit/13a9f9d))
+
+
+
# [3.0.0-beta.4](https://github.com/aurelia/i18n/compare/3.0.0-beta.3...3.0.0-beta.4) (2018-11-23)
diff --git a/package.json b/package.json
index 5482543f..fbbe0753 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "aurelia-i18n",
- "version": "3.0.0-beta.4",
+ "version": "3.0.0-beta.5",
"description": "A plugin that provides i18n support.",
"keywords": [
"aurelia",