[I18n] Update locale objects format (#23917) (#24639)

* Update output file format for i18n_check tool

* Update i18n engine to work with new format

* Update tests

* Fix UI bootstrap test

* Update loader tests and fix getTranslationsByLocale function

* Fix messages passing to IntlProvider

* Update messages interface

* Resolve comments

* Resolve comment
This commit is contained in:
Leanid Shutau 2018-10-29 17:24:28 +03:00 committed by GitHub
parent 31d30f814e
commit b3086f294b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 293 additions and 288 deletions

View file

@ -1,4 +1,6 @@
{
"a.b.c": "bar",
"d.e.f": "foo"
"messages": {
"a.b.c": "bar",
"d.e.f": "foo"
}
}

View file

@ -1,4 +1,6 @@
{
"a.b.c": "foo",
"d.e.f": "bar"
"messages": {
"a.b.c": "foo",
"d.e.f": "bar"
}
}

View file

@ -1,4 +1,6 @@
{
"a.b.c.custom": "foo.custom",
"d.e.f.custom": "bar.custom"
"messages": {
"a.b.c.custom": "foo.custom",
"d.e.f.custom": "bar.custom"
}
}

View file

@ -1,3 +1,5 @@
{
test: 'test' // JSON5 test
messages: {
test: 'test' // JSON5 test
}
}

View file

@ -1,3 +1,5 @@
{
"test": "test"
"messages": {
"test": "test"
}
}

View file

@ -22,8 +22,8 @@ import * as i18n from '../core';
export type I18nServiceType = ReturnType<I18nProvider['$get']>;
export class I18nProvider implements angular.IServiceProvider {
public addMessages = i18n.addMessages;
public getMessages = i18n.getMessages;
public addTranslation = i18n.addTranslation;
public getTranslation = i18n.getTranslation;
public setLocale = i18n.setLocale;
public getLocale = i18n.getLocale;
public setDefaultLocale = i18n.setDefaultLocale;

View file

@ -4,7 +4,7 @@ exports[`I18n engine addMessages should throw error if locale is not specified o
exports[`I18n engine addMessages should throw error if locale is not specified or empty 2`] = `"[I18n] A \`locale\` must be a non-empty string to add messages."`;
exports[`I18n engine addMessages should throw error if locale specified in messages is different from one provided as second argument 1`] = `"[I18n] A \`locale\` in the messages object is different from the one provided as a second argument."`;
exports[`I18n engine addMessages should throw error if locale specified in messages is different from one provided as second argument 1`] = `"[I18n] A \`locale\` in the translation object is different from the one provided as a second argument."`;
exports[`I18n engine translate should throw error if id is not a non-empty string 1`] = `"[I18n] An \`id\` must be a non-empty string to translate a message."`;

View file

@ -33,46 +33,54 @@ describe('I18n engine', () => {
describe('addMessages', () => {
test('should throw error if locale is not specified or empty', () => {
expect(() => i18n.addMessages({ foo: 'bar' })).toThrowErrorMatchingSnapshot();
expect(() => i18n.addMessages({ locale: '' })).toThrowErrorMatchingSnapshot();
expect(() =>
i18n.addTranslation({ messages: { foo: 'bar' } })
).toThrowErrorMatchingSnapshot();
expect(() =>
i18n.addTranslation({ locale: '', messages: {} })
).toThrowErrorMatchingSnapshot();
});
test('should throw error if locale specified in messages is different from one provided as second argument', () => {
expect(() =>
i18n.addMessages({ foo: 'bar', locale: 'en' }, 'ru')
i18n.addTranslation({ messages: { foo: 'bar' }, locale: 'en' }, 'ru')
).toThrowErrorMatchingSnapshot();
});
test('should add messages if locale prop is passed as second argument', () => {
const locale = 'ru';
expect(i18n.getMessages()).toEqual({});
expect(i18n.getTranslation()).toEqual({ messages: {} });
i18n.addMessages({ foo: 'bar' }, locale);
i18n.addTranslation({ messages: { foo: 'bar' } }, locale);
expect(i18n.getMessages()).toEqual({});
expect(i18n.getTranslation()).toEqual({ messages: {} });
i18n.setLocale(locale);
expect(i18n.getMessages()).toEqual({ foo: 'bar' });
expect(i18n.getTranslation()).toEqual({ messages: { foo: 'bar' } });
});
test('should add messages if locale prop is passed as messages property', () => {
const locale = 'ru';
expect(i18n.getMessages()).toEqual({});
expect(i18n.getTranslation()).toEqual({ messages: {} });
i18n.addMessages({
i18n.addTranslation({
locale,
foo: 'bar',
messages: {
foo: 'bar',
},
});
expect(i18n.getMessages()).toEqual({});
expect(i18n.getTranslation()).toEqual({ messages: {} });
i18n.setLocale(locale);
expect(i18n.getMessages()).toEqual({
foo: 'bar',
expect(i18n.getTranslation()).toEqual({
messages: {
foo: 'bar',
},
locale: 'ru',
});
});
@ -81,25 +89,33 @@ describe('I18n engine', () => {
const locale = 'ru';
i18n.setLocale(locale);
i18n.addMessages({
i18n.addTranslation({
locale,
['a.b.c']: 'foo',
messages: {
['a.b.c']: 'foo',
},
});
expect(i18n.getMessages()).toEqual({
expect(i18n.getTranslation()).toEqual({
locale: 'ru',
['a.b.c']: 'foo',
messages: {
['a.b.c']: 'foo',
},
});
i18n.addMessages({
i18n.addTranslation({
locale,
['d.e.f']: 'bar',
messages: {
['d.e.f']: 'bar',
},
});
expect(i18n.getMessages()).toEqual({
expect(i18n.getTranslation()).toEqual({
locale: 'ru',
['a.b.c']: 'foo',
['d.e.f']: 'bar',
messages: {
['a.b.c']: 'foo',
['d.e.f']: 'bar',
},
});
});
@ -107,24 +123,32 @@ describe('I18n engine', () => {
const locale = 'ru';
i18n.setLocale(locale);
i18n.addMessages({
i18n.addTranslation({
locale,
['a.b.c']: 'foo',
messages: {
['a.b.c']: 'foo',
},
});
expect(i18n.getMessages()).toEqual({
expect(i18n.getTranslation()).toEqual({
locale: 'ru',
['a.b.c']: 'foo',
messages: {
['a.b.c']: 'foo',
},
});
i18n.addMessages({
i18n.addTranslation({
locale,
['a.b.c']: 'bar',
messages: {
['a.b.c']: 'bar',
},
});
expect(i18n.getMessages()).toEqual({
expect(i18n.getTranslation()).toEqual({
locale: 'ru',
['a.b.c']: 'bar',
messages: {
['a.b.c']: 'bar',
},
});
});
@ -132,52 +156,64 @@ describe('I18n engine', () => {
const locale = 'en-us';
i18n.setLocale(locale);
i18n.addMessages(
i18n.addTranslation(
{
['a.b.c']: 'bar',
messages: {
['a.b.c']: 'bar',
},
},
'en_US'
);
expect(i18n.getLocale()).toBe(locale);
expect(i18n.getMessages()).toEqual({
['a.b.c']: 'bar',
expect(i18n.getTranslation()).toEqual({
messages: {
['a.b.c']: 'bar',
},
});
});
});
describe('getMessages', () => {
describe('getTranslation', () => {
test('should return messages for the current language', () => {
i18n.addMessages({
i18n.addTranslation({
locale: 'ru',
foo: 'bar',
messages: {
foo: 'bar',
},
});
i18n.addMessages({
i18n.addTranslation({
locale: 'en',
bar: 'foo',
messages: {
bar: 'foo',
},
});
i18n.setLocale('ru');
expect(i18n.getMessages()).toEqual({
expect(i18n.getTranslation()).toEqual({
locale: 'ru',
foo: 'bar',
messages: {
foo: 'bar',
},
});
i18n.setLocale('en');
expect(i18n.getMessages()).toEqual({
expect(i18n.getTranslation()).toEqual({
locale: 'en',
bar: 'foo',
messages: {
bar: 'foo',
},
});
});
test('should return an empty object if messages for current locale are not specified', () => {
expect(i18n.getMessages()).toEqual({});
expect(i18n.getTranslation()).toEqual({ messages: {} });
i18n.setLocale('fr');
expect(i18n.getMessages()).toEqual({});
expect(i18n.getTranslation()).toEqual({ messages: {} });
i18n.setLocale('en');
expect(i18n.getMessages()).toEqual({});
expect(i18n.getTranslation()).toEqual({ messages: {} });
});
});
@ -352,22 +388,25 @@ describe('I18n engine', () => {
});
test('should return array of registered locales', () => {
i18n.addMessages({
i18n.addTranslation({
locale: 'en',
messages: {},
});
expect(i18n.getRegisteredLocales()).toEqual(['en']);
i18n.addMessages({
i18n.addTranslation({
locale: 'ru',
messages: {},
});
expect(i18n.getRegisteredLocales()).toContain('en');
expect(i18n.getRegisteredLocales()).toContain('ru');
expect(i18n.getRegisteredLocales().length).toBe(2);
i18n.addMessages({
i18n.addTranslation({
locale: 'fr',
messages: {},
});
expect(i18n.getRegisteredLocales()).toContain('en');
@ -394,7 +433,9 @@ describe('I18n engine', () => {
test('should return message as is if values are not provided', () => {
i18n.init({
locale: 'en',
['a.b.c']: 'foo',
messages: {
['a.b.c']: 'foo',
},
});
expect(i18n.translate('a.b.c')).toBe('foo');
@ -407,7 +448,9 @@ describe('I18n engine', () => {
test('should not return defaultMessage as is if values are provided', () => {
i18n.init({
locale: 'en',
['a.b.c']: 'foo',
messages: {
['a.b.c']: 'foo',
},
});
expect(i18n.translate('a.b.c', { defaultMessage: 'bar' })).toBe('foo');
});
@ -415,8 +458,10 @@ describe('I18n engine', () => {
test('should interpolate variables', () => {
i18n.init({
locale: 'en',
['a.b.c']: 'foo {a}, {b}, {c} bar',
['d.e.f']: '{foo}',
messages: {
['a.b.c']: 'foo {a}, {b}, {c} bar',
['d.e.f']: '{foo}',
},
});
expect(
@ -440,11 +485,13 @@ describe('I18n engine', () => {
test('should format pluralized messages', () => {
i18n.init({
locale: 'en',
['a.b.c']: `You have {numPhotos, plural,
messages: {
['a.b.c']: `You have {numPhotos, plural,
=0 {no photos.}
=1 {one photo.}
other {# photos.}
}`,
},
});
expect(i18n.translate('a.b.c', { values: { numPhotos: 0 } })).toBe('You have no photos.');
@ -494,11 +541,13 @@ describe('I18n engine', () => {
test('should throw error if wrong context is provided to the translation string', () => {
i18n.init({
locale: 'en',
['a.b.c']: `You have {numPhotos, plural,
messages: {
['a.b.c']: `You have {numPhotos, plural,
=0 {no photos.}
=1 {one photo.}
other {# photos.}
}`,
},
});
i18n.setDefaultLocale('en');
@ -519,7 +568,9 @@ describe('I18n engine', () => {
test('should format messages with percent formatter', () => {
i18n.init({
locale: 'en',
['a.b.c']: 'Result: {result, number, percent}',
messages: {
['a.b.c']: 'Result: {result, number, percent}',
},
});
i18n.setDefaultLocale('en');
@ -536,10 +587,12 @@ describe('I18n engine', () => {
test('should format messages with date formatter', () => {
i18n.init({
locale: 'en',
['a.short']: 'Sale begins {start, date, short}',
['a.medium']: 'Sale begins {start, date, medium}',
['a.long']: 'Sale begins {start, date, long}',
['a.full']: 'Sale begins {start, date, full}',
messages: {
['a.short']: 'Sale begins {start, date, short}',
['a.medium']: 'Sale begins {start, date, medium}',
['a.long']: 'Sale begins {start, date, long}',
['a.full']: 'Sale begins {start, date, full}',
},
});
expect(
@ -602,8 +655,10 @@ describe('I18n engine', () => {
test('should format messages with time formatter', () => {
i18n.init({
locale: 'en',
['a.short']: 'Coupon expires at {expires, time, short}',
['a.medium']: 'Coupon expires at {expires, time, medium}',
messages: {
['a.short']: 'Coupon expires at {expires, time, short}',
['a.medium']: 'Coupon expires at {expires, time, medium}',
},
});
expect(
@ -645,8 +700,10 @@ describe('I18n engine', () => {
usd: { style: 'currency', currency: 'USD' },
},
},
['a.b.c']: 'Your total is {total, number, usd}',
['d.e.f']: 'Your total is {total, number, eur}',
messages: {
['a.b.c']: 'Your total is {total, number, usd}',
['d.e.f']: 'Your total is {total, number, eur}',
},
});
expect(i18n.translate('a.b.c', { values: { total: 1000 } })).toBe('Your total is $1,000.00');
@ -670,6 +727,7 @@ describe('I18n engine', () => {
usd: { style: 'currency', currency: 'USD' },
},
},
messages: {},
});
i18n.setDefaultLocale('en');
@ -704,7 +762,9 @@ describe('I18n engine', () => {
test('should use default format if passed format option is not specified', () => {
i18n.init({
locale: 'en',
['a.b.c']: 'Your total is {total, number, usd}',
messages: {
['a.b.c']: 'Your total is {total, number, usd}',
},
});
i18n.setDefaultLocale('en');
@ -721,7 +781,9 @@ describe('I18n engine', () => {
test('should throw error if used format is not specified', () => {
i18n.init({
locale: 'en',
['a.b.c']: 'Your total is {total, foo}',
messages: {
['a.b.c']: 'Your total is {total, foo}',
},
});
i18n.setDefaultLocale('en');
@ -741,28 +803,32 @@ describe('I18n engine', () => {
describe('init', () => {
test('should not initialize the engine if messages are not specified', () => {
i18n.init();
expect(i18n.getMessages()).toEqual({});
expect(i18n.getTranslation()).toEqual({ messages: {} });
});
test('should throw error if messages are empty', () => {
expect(() => i18n.init({})).toThrow();
expect(i18n.getMessages()).toEqual({});
expect(() => i18n.init({ messages: {} })).toThrow();
expect(i18n.getTranslation()).toEqual({ messages: {} });
});
test('should add messages if locale is specified', () => {
i18n.init({
locale: 'en',
foo: 'bar',
messages: {
foo: 'bar',
},
});
expect(i18n.getMessages()).toEqual({
expect(i18n.getTranslation()).toEqual({
locale: 'en',
foo: 'bar',
messages: {
foo: 'bar',
},
});
});
test('should set the current locale', () => {
i18n.init({ locale: 'ru' });
i18n.init({ locale: 'ru', messages: {} });
expect(i18n.getLocale()).toBe('ru');
});
@ -778,6 +844,7 @@ describe('I18n engine', () => {
},
},
},
messages: {},
});
expect((i18n.getFormats().date as any).custom).toEqual({

View file

@ -21,7 +21,7 @@ import memoizeIntlConstructor from 'intl-format-cache';
import IntlMessageFormat from 'intl-messageformat';
import IntlRelativeFormat from 'intl-relativeformat';
import { Messages, PlainMessages } from '../messages';
import { Translation } from '../translation';
import { Formats, formats as EN_FORMATS } from './formats';
import { hasValues, isObject, isString, mergeAll } from './helper';
import { isPseudoLocale, translateUsingPseudoLocale } from './pseudo_locale';
@ -31,7 +31,7 @@ import './locales.js';
const EN_LOCALE = 'en';
const LOCALE_DELIMITER = '-';
const messages: Messages = {};
const translationsForLocale: Record<string, Translation> = {};
const getMessageFormat = memoizeIntlConstructor(IntlMessageFormat);
let defaultLocale = EN_LOCALE;
@ -45,8 +45,9 @@ IntlRelativeFormat.defaultLocale = defaultLocale;
* Returns message by the given message id.
* @param id - path to the message
*/
function getMessageById(id: string): string {
return getMessages()[id];
function getMessageById(id: string): string | undefined {
const translation = getTranslation();
return translation.messages ? translation.messages[id] : undefined;
}
/**
@ -59,33 +60,38 @@ function normalizeLocale(locale: string) {
/**
* Provides a way to register translations with the engine
* @param newMessages
* @param newTranslation
* @param [locale = messages.locale]
*/
export function addMessages(newMessages: PlainMessages = {}, locale = newMessages.locale) {
export function addTranslation(newTranslation: Translation, locale = newTranslation.locale) {
if (!locale || !isString(locale)) {
throw new Error('[I18n] A `locale` must be a non-empty string to add messages.');
}
if (newMessages.locale && newMessages.locale !== locale) {
if (newTranslation.locale && newTranslation.locale !== locale) {
throw new Error(
'[I18n] A `locale` in the messages object is different from the one provided as a second argument.'
'[I18n] A `locale` in the translation object is different from the one provided as a second argument.'
);
}
const normalizedLocale = normalizeLocale(locale);
const existingTranslation = translationsForLocale[normalizedLocale] || { messages: {} };
messages[normalizedLocale] = {
...messages[normalizedLocale],
...newMessages,
translationsForLocale[normalizedLocale] = {
formats: newTranslation.formats || existingTranslation.formats,
locale: newTranslation.locale || existingTranslation.locale,
messages: {
...existingTranslation.messages,
...newTranslation.messages,
},
};
}
/**
* Returns messages for the current language
*/
export function getMessages(): PlainMessages {
return messages[currentLocale] || {};
export function getTranslation(): Translation {
return translationsForLocale[currentLocale] || { messages: {} };
}
/**
@ -155,7 +161,7 @@ export function getFormats() {
* Returns array of locales having translations
*/
export function getRegisteredLocales() {
return Object.keys(messages);
return Object.keys(translationsForLocale);
}
interface TranslateArguments {
@ -218,20 +224,20 @@ export function translate(
/**
* Initializes the engine
* @param newMessages
* @param newTranslation
*/
export function init(newMessages?: PlainMessages) {
if (!newMessages) {
export function init(newTranslation?: Translation) {
if (!newTranslation) {
return;
}
addMessages(newMessages);
addTranslation(newTranslation);
if (newMessages.locale) {
setLocale(newMessages.locale);
if (newTranslation.locale) {
setLocale(newTranslation.locale);
}
if (newMessages.formats) {
setFormats(newMessages.formats);
if (newTranslation.formats) {
setFormats(newTranslation.formats);
}
}

View file

@ -100,8 +100,10 @@ describe('I18n loader', () => {
expect(await i18nLoader.getTranslationsByLocale('en')).toEqual({
locale: 'en',
['a.b.c']: 'foo',
['d.e.f']: 'bar',
messages: {
['a.b.c']: 'foo',
['d.e.f']: 'bar',
},
});
});
@ -110,7 +112,7 @@ describe('I18n loader', () => {
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json')
);
expect(await i18nLoader.getTranslationsByLocale('ru')).toEqual({});
expect(await i18nLoader.getTranslationsByLocale('ru')).toEqual({ messages: {} });
});
test('should return translation messages from a couple of files by specified locale', async () => {
@ -121,10 +123,12 @@ describe('I18n loader', () => {
expect(await i18nLoader.getTranslationsByLocale('en')).toEqual({
locale: 'en',
['a.b.c']: 'foo',
['d.e.f']: 'bar',
['a.b.c.custom']: 'foo.custom',
['d.e.f.custom']: 'bar.custom',
messages: {
['a.b.c']: 'foo',
['d.e.f']: 'bar',
['a.b.c.custom']: 'foo.custom',
['d.e.f.custom']: 'bar.custom',
},
});
});
@ -138,21 +142,27 @@ describe('I18n loader', () => {
expect(await i18nLoader.getTranslationsByLocale('en')).toEqual({
locale: 'en',
['a.b.c']: 'foo',
['d.e.f']: 'bar',
['a.b.c.custom']: 'foo.custom',
['d.e.f.custom']: 'bar.custom',
messages: {
['a.b.c']: 'foo',
['d.e.f']: 'bar',
['a.b.c.custom']: 'foo.custom',
['d.e.f.custom']: 'bar.custom',
},
});
expect(await i18nLoader.getTranslationsByLocale('en-US')).toEqual({
locale: 'en-US',
['a.b.c']: 'bar',
['d.e.f']: 'foo',
messages: {
['a.b.c']: 'bar',
['d.e.f']: 'foo',
},
});
expect(await i18nLoader.getTranslationsByLocale('ru')).toEqual({
locale: 'ru',
test: 'test',
messages: {
test: 'test',
},
});
});
@ -163,7 +173,9 @@ describe('I18n loader', () => {
expect(await i18nLoader.getTranslationsByLocale('fr')).toEqual({
locale: 'fr',
test: 'test',
messages: {
test: 'test',
},
});
});
});
@ -180,19 +192,25 @@ describe('I18n loader', () => {
expect(await i18nLoader.getAllTranslations()).toEqual({
en: {
locale: 'en',
['a.b.c']: 'foo',
['d.e.f']: 'bar',
['a.b.c.custom']: 'foo.custom',
['d.e.f.custom']: 'bar.custom',
messages: {
['a.b.c']: 'foo',
['d.e.f']: 'bar',
['a.b.c.custom']: 'foo.custom',
['d.e.f.custom']: 'bar.custom',
},
},
['en-US']: {
locale: 'en-US',
['a.b.c']: 'bar',
['d.e.f']: 'foo',
messages: {
['a.b.c']: 'bar',
['d.e.f']: 'foo',
},
},
ru: {
locale: 'ru',
test: 'test',
messages: {
test: 'test',
},
},
});
});
@ -214,19 +232,25 @@ describe('I18n loader', () => {
).toEqual({
en: {
locale: 'en',
['a.b.c']: 'foo',
['d.e.f']: 'bar',
['a.b.c.custom']: 'foo.custom',
['d.e.f.custom']: 'bar.custom',
messages: {
['a.b.c']: 'foo',
['d.e.f']: 'bar',
['a.b.c.custom']: 'foo.custom',
['d.e.f.custom']: 'bar.custom',
},
},
['en-US']: {
locale: 'en-US',
['a.b.c']: 'bar',
['d.e.f']: 'foo',
messages: {
['a.b.c']: 'bar',
['d.e.f']: 'foo',
},
},
ru: {
locale: 'ru',
test: 'test',
messages: {
test: 'test',
},
},
});
});

View file

@ -23,7 +23,7 @@ import * as path from 'path';
import { promisify } from 'util';
import { unique } from './core/helper';
import { Messages, PlainMessages } from './messages';
import { Translation } from './translation';
const asyncReadFile = promisify(readFile);
@ -39,7 +39,7 @@ const translationsRegistry: { [key: string]: string[] } = {};
* Internal property for caching loaded translations files.
* Key is path to translation file, value is object with translation messages
*/
const loadedFiles: { [key: string]: PlainMessages } = {};
const loadedFiles: { [key: string]: Translation } = {};
/**
* Returns locale by the given translation file name
@ -69,7 +69,7 @@ function getLocaleFromFileName(fullFileName: string) {
* @param pathToFile
* @returns
*/
async function loadFile(pathToFile: string): Promise<PlainMessages> {
async function loadFile(pathToFile: string): Promise<Translation> {
return JSON5.parse(await asyncReadFile(pathToFile, 'utf8'));
}
@ -127,7 +127,7 @@ export function getRegisteredLocales() {
* @param locale
* @returns translation messages
*/
export async function getTranslationsByLocale(locale: string): Promise<PlainMessages> {
export async function getTranslationsByLocale(locale: string): Promise<Translation> {
const files = translationsRegistry[locale] || [];
const notLoadedFiles = files.filter(file => !loadedFiles[file]);
@ -135,15 +135,21 @@ export async function getTranslationsByLocale(locale: string): Promise<PlainMess
await loadAndCacheFiles(notLoadedFiles);
}
return files.length
? files.reduce(
(messages, file) => ({
...messages,
...loadedFiles[file],
}),
{ locale }
)
: {};
if (!files.length) {
return { messages: {} };
}
return files.reduce(
(translation: Translation, file) => ({
locale: loadedFiles[file].locale || translation.locale,
formats: loadedFiles[file].formats || translation.formats,
messages: {
...loadedFiles[file].messages,
...translation.messages,
},
}),
{ locale, messages: {} }
);
}
/**
@ -151,7 +157,7 @@ export async function getTranslationsByLocale(locale: string): Promise<PlainMess
* @returns A Promise object
* where keys are the locale and values are objects of translation messages
*/
export async function getAllTranslations(): Promise<{ [key: string]: Messages }> {
export async function getAllTranslations(): Promise<{ [key: string]: Translation }> {
const locales = getRegisteredLocales();
const translations = await Promise.all(locales.map(getTranslationsByLocale));

View file

@ -66,62 +66,7 @@ Object {
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {
"date": Object {
"full": Object {
"day": "numeric",
"month": "long",
"weekday": "long",
"year": "numeric",
},
"long": Object {
"day": "numeric",
"month": "long",
"year": "numeric",
},
"medium": Object {
"day": "numeric",
"month": "short",
"year": "numeric",
},
"short": Object {
"day": "numeric",
"month": "numeric",
"year": "2-digit",
},
},
"number": Object {
"currency": Object {
"style": "currency",
},
"percent": Object {
"style": "percent",
},
},
"time": Object {
"full": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"long": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"medium": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
},
"short": Object {
"hour": "numeric",
"minute": "numeric",
},
},
},
"formats": Object {},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],

View file

@ -66,62 +66,7 @@ Object {
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {
"date": Object {
"full": Object {
"day": "numeric",
"month": "long",
"weekday": "long",
"year": "numeric",
},
"long": Object {
"day": "numeric",
"month": "long",
"year": "numeric",
},
"medium": Object {
"day": "numeric",
"month": "short",
"year": "numeric",
},
"short": Object {
"day": "numeric",
"month": "numeric",
"year": "2-digit",
},
},
"number": Object {
"currency": Object {
"style": "currency",
},
"percent": Object {
"style": "percent",
},
},
"time": Object {
"full": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"long": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"medium": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
},
"short": Object {
"hour": "numeric",
"minute": "numeric",
},
},
},
"formats": Object {},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],

View file

@ -58,9 +58,9 @@ export class I18nProvider extends React.PureComponent {
return (
<IntlProvider
locale={i18n.getLocale()}
messages={i18n.getMessages()}
messages={i18n.getTranslation().messages}
defaultLocale={i18n.getDefaultLocale()}
formats={i18n.getFormats()}
formats={i18n.getTranslation().formats}
defaultFormats={i18n.getFormats()}
textComponent={React.Fragment}
>

View file

@ -19,21 +19,17 @@
import { Formats } from './core/formats';
/**
* Messages tree, where leafs are translated strings
*/
export interface Messages {
[key: string]: PlainMessages;
}
export interface PlainMessages {
[key: string]: any;
export interface Translation {
/**
* locale of the messages
* Actual translated messages.
*/
messages: Record<string, string>;
/**
* Locale of the translated messages.
*/
locale?: string;
/**
* set of options to the underlying formatter
* Set of options to the underlying formatter.
*/
formats?: Formats;
}

View file

@ -58,10 +58,12 @@ exports[`dev/i18n/serializers/json should serialize default messages to JSON 1`]
}
}
},
\\"plugin1.message.id-1\\": \\"Message text 1 \\",
\\"plugin2.message.id-2\\": {
\\"text\\": \\"Message text 2\\",
\\"comment\\": \\"Message context\\"
\\"messages\\": {
\\"plugin1.message.id-1\\": \\"Message text 1 \\",
\\"plugin2.message.id-2\\": {
\\"text\\": \\"Message text 2\\",
\\"comment\\": \\"Message context\\"
}
}
}"
`;

View file

@ -58,8 +58,10 @@ exports[`dev/i18n/serializers/json5 should serialize default messages to JSON5 1
},
},
},
'plugin1.message.id-1': 'Message text 1',
'plugin2.message.id-2': 'Message text 2', // Message context
messages: {
'plugin1.message.id-1': 'Message text 1',
'plugin2.message.id-2': 'Message text 2', // Message context
},
}
"
`;

View file

@ -20,13 +20,13 @@
import { i18n } from '@kbn/i18n';
export function serializeToJson(defaultMessages) {
const resultJsonObject = { formats: i18n.formats };
const resultJsonObject = { formats: i18n.formats, messages: {} };
for (const [mapKey, mapValue] of defaultMessages) {
if (mapValue.context) {
resultJsonObject[mapKey] = { text: mapValue.message, comment: mapValue.context };
resultJsonObject.messages[mapKey] = { text: mapValue.message, comment: mapValue.context };
} else {
resultJsonObject[mapKey] = mapValue.message;
resultJsonObject.messages[mapKey] = mapValue.message;
}
}

View file

@ -23,9 +23,11 @@ import { i18n } from '@kbn/i18n';
const ESCAPE_SINGLE_QUOTE_REGEX = /\\([\s\S])|(')/g;
export function serializeToJson5(defaultMessages) {
// .slice(0, -1): remove closing curly brace from json to append messages
// .slice(0, -4): remove closing curly braces from json to append messages
let jsonBuffer = Buffer.from(
JSON5.stringify({ formats: i18n.formats }, { quote: `'`, space: 2 }).slice(0, -1)
JSON5.stringify({ formats: i18n.formats, messages: {} }, { quote: `'`, space: 2 })
.slice(0, -4)
.concat('\n')
);
for (const [mapKey, mapValue] of defaultMessages) {
@ -36,13 +38,13 @@ export function serializeToJson5(defaultMessages) {
jsonBuffer = Buffer.concat([
jsonBuffer,
Buffer.from(` '${mapKey}': '${formattedMessage}',`),
Buffer.from(` '${mapKey}': '${formattedMessage}',`),
Buffer.from(formattedContext ? ` // ${formattedContext}\n` : '\n'),
]);
}
// append previously removed closing curly brace
jsonBuffer = Buffer.concat([jsonBuffer, Buffer.from('}\n')]);
// append previously removed closing curly braces
jsonBuffer = Buffer.concat([jsonBuffer, Buffer.from(' },\n}\n')]);
return jsonBuffer;
}