shaka-player
Version:
DASH/EME video player library
509 lines (413 loc) • 19.3 kB
JavaScript
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
describe('Localization', () => {
const Localization = shaka.ui.Localization;
// https://bit.ly/2PbzxlD
const DWARFISH = 'dwarfish';
const DWARFISH_NORTH = 'dwarfish-NORTH';
const DWARFISH_SOUTH = 'dwarfish-SOUTH';
const ELVISH = 'elvish';
const ELVISH_WOODLAND = 'elvish-WOODLAND';
const HALFLING = 'halfling';
const HALFLING_COMMON = 'halfling-COMMON';
const HALFLING_RIVER = 'halfling-RIVER';
const HALFLING_SHIRE = 'halfling-SHIRE';
describe('insert', () => {
const EN_US = 'en-US';
it('can add new data', () => {
/** @type {!shaka.ui.Localization} */
const localization = new Localization(EN_US);
localization.insert(EN_US, new Map([
['hello', 'howdy'],
['good-bye', 'cheerio'],
]));
expect(localization.resolve('hello')).toBe('howdy');
expect(localization.resolve('good-bye')).toBe('cheerio');
});
it('can replace old data when adding new data', () => {
/** @type {!shaka.ui.Localization} */
const localization = new Localization(EN_US);
localization.insert(EN_US, new Map([
['hello', 'howdy'],
['good-bye', 'cheerio'],
]));
expect(localization.resolve('hello')).toBe('howdy');
expect(localization.resolve('good-bye')).toBe('cheerio');
localization.insert(EN_US, new Map([
['good-bye', 'farewell'],
['thank-you', 'thank-you'],
]));
expect(localization.resolve('hello')).toBe('howdy');
expect(localization.resolve('good-bye')).toBe('farewell');
expect(localization.resolve('thank-you')).toBe('thank-you');
});
it('can keep old data when adding new', () => {
const USE_OLD = Localization.ConflictResolution.USE_OLD;
/** @type {!shaka.ui.Localization} */
const localization = new Localization(EN_US);
localization.insert(EN_US, new Map([
['hello', 'howdy'],
['good-bye', 'cheerio'],
]));
expect(localization.resolve('hello')).toBe('howdy');
expect(localization.resolve('good-bye')).toBe('cheerio');
localization.insert(EN_US, new Map([
['hello', 'greetings'],
['good-bye', 'farewell'],
['thank-you', 'thank-you'],
]), USE_OLD);
// Nothing should have changed.
expect(localization.resolve('hello')).toBe('howdy');
expect(localization.resolve('good-bye')).toBe('cheerio');
expect(localization.resolve('thank-you')).toBe('thank-you');
});
});
describe('locale fallback', () => {
const KEY = 'KEY';
const FALLBACK = 'FALLBACK';
const FALLBACK_VALUE = 'FALLBACK VALUE';
const ELVISH_VALUE = 'ELVISH VALUE';
const HALFLING_COMMON_VALUE = 'HALFLING-COMMON VALUE';
const HALFLING_SHIRE_VALUE = 'HALFLING-SHIRE VALUE';
/** @type {!shaka.ui.Localization} */
let localization;
const insert = (locale, value) => {
localization.insert(locale, new Map([[KEY, value]]));
};
const testLocales = (locales, expectedValue) => {
localization.changeLocale(locales);
const value = localization.resolve(KEY);
expect(value).toBe(expectedValue);
};
beforeEach(() => {
localization = new Localization(FALLBACK);
// Insert translations to test various scenarios below:
insert(FALLBACK, FALLBACK_VALUE);
insert(ELVISH, ELVISH_VALUE);
insert(HALFLING_COMMON, HALFLING_COMMON_VALUE);
insert(HALFLING_SHIRE, HALFLING_SHIRE_VALUE);
});
it('resorts to final fallback for one unknown locale', () => {
// For one locale we don't have translations for, go to the fallback.
testLocales([DWARFISH_NORTH], FALLBACK_VALUE);
});
it('resorts to final fallback for multiple unknown locales', () => {
// For multiple locales in which we don't have translations, go to the
// fallback.
testLocales([
DWARFISH_NORTH,
DWARFISH_SOUTH,
], FALLBACK_VALUE);
});
it('prefers a known locale from the list instead of final fallback', () => {
// If we have a locale in the list for which we _do_ have translations,
// expect to see the translated value.
testLocales([
DWARFISH_NORTH,
DWARFISH_SOUTH,
ELVISH,
], ELVISH_VALUE);
});
it('prefers the first known locale from the list', () => {
// If we have multiple locales in the list for which we _do_ have
// translations, expect to see the translated value from the first one.
testLocales([
DWARFISH_NORTH,
DWARFISH_SOUTH,
HALFLING_COMMON,
ELVISH,
], HALFLING_COMMON_VALUE);
});
it('finds translations from the parent locale', () => {
// If we have a locale in the list for which we have a region-less
// translation, expect to see the translated value.
testLocales([ELVISH_WOODLAND], ELVISH_VALUE);
});
it('finds translations from a locale with a different region', () => {
// If there's a locale in the list for which we have translations in a
// sibling locale (same language, different region), expect to see that
// value. Common is chosen over shire because equivalent-distance locales
// are alphabetically sorted to break ties.
testLocales([HALFLING_RIVER], HALFLING_COMMON_VALUE);
});
it('finds translations from a child locale', () => {
// If there's a locale in the list for which we have translations in a
// child locale (same language, but for a specific region), expect to see
// that value. Common is chosen over shire because equivalent-distance
// locales are alphabetically sorted to break ties.
testLocales([HALFLING], HALFLING_COMMON_VALUE);
});
it('prefers a relative of an earlier locale to an exact later one', () => {
// If we have multiple locales in the list, and an earlier one has
// relatives with translations, expect to see that chosen over an exact
// match later in the list.
testLocales([
HALFLING,
ELVISH,
], HALFLING_COMMON_VALUE);
});
});
describe('unknown locales event', () => {
it('fires when we change to a locale we have not loaded', () => {
const events = [];
const localization = new Localization(/* fallback= */ HALFLING_COMMON);
collectEvents(localization, Localization.UNKNOWN_LOCALES, events);
localization.insert(HALFLING_COMMON, new Map());
localization.changeLocale([ELVISH_WOODLAND]);
expect(events.length).toBe(1);
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
});
it('will not fire after we add the locale', () => {
const events = [];
const localization = new Localization(/* fallback= */ HALFLING_COMMON);
collectEvents(localization, Localization.UNKNOWN_LOCALES, events);
// We should see an event telling us that both elvish and dwarfish are
// missing.
localization.insert(HALFLING_COMMON, new Map());
localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
expect(events.length).toBe(1);
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
// We should see an event telling us that dwarfish is still missing.
localization.insert(ELVISH_WOODLAND, new Map());
localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
expect(events.length).toBe(2);
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
expect(events[1]['locales']).toEqual([DWARFISH_NORTH]);
// There should be no more events now that we added the last language.
localization.insert(DWARFISH_NORTH, new Map());
localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
expect(events.length).toBe(2);
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
expect(events[1]['locales']).toEqual([DWARFISH_NORTH]);
});
});
describe('unknown localization event', () => {
const HOW_ARE_YOU = 'how are you';
const HALFLING_COMMON_MAP = new Map().set(
HOW_ARE_YOU, 'How ye this topa the mornin?');
const HALFLING_SHIRE_MAP = new Map().set(
HOW_ARE_YOU, 'How be the day treating ya?');
const DWARFISH_MAP = new Map().set(
HOW_ARE_YOU, 'You alive?');
const DWARFISH_NORTH_MAP = new Map().set(
HOW_ARE_YOU, 'Is the fire alive in ye soul?');
const DWARFISH_SOUTH_MAP = new Map().set(
HOW_ARE_YOU, 'You dead yet?');
/** @type {!shaka.ui.Localization} */
let localization;
beforeEach(() => {
localization = new shaka.ui.Localization(HALFLING_COMMON);
// Insert every locale into the system but leave them empty. Each test
// will add entries as they need them.
const emptyMap = new Map();
localization.insert(DWARFISH, emptyMap);
localization.insert(DWARFISH_NORTH, emptyMap);
localization.insert(DWARFISH_SOUTH, emptyMap);
localization.insert(HALFLING_COMMON, emptyMap);
localization.insert(HALFLING_SHIRE, emptyMap);
});
it('will not fire when key is found in preferred', () => {
localization.insert(DWARFISH_NORTH, DWARFISH_NORTH_MAP);
localization.changeLocale([DWARFISH_NORTH]);
const events = [];
collectEvents(localization, Localization.UNKNOWN_LOCALIZATION, events);
// We expect to see a non-empty string returned and to see NO "missing
// localization" events.
expect(localization.resolve(HOW_ARE_YOU)).toBeTruthy();
expect(events.length).toBe(0);
});
it('will not fire when key is found in base', () => {
localization.insert(DWARFISH, DWARFISH_MAP);
localization.changeLocale([DWARFISH_NORTH]);
const events = [];
collectEvents(localization, Localization.UNKNOWN_LOCALIZATION, events);
// We expect to see a non-empty string returned and to see NO "missing
// localization" events.
expect(localization.resolve(HOW_ARE_YOU)).toBeTruthy();
expect(events.length).toBe(0);
});
it('will not fire when key is found in sibling', () => {
localization.insert(DWARFISH_SOUTH, DWARFISH_SOUTH_MAP);
localization.changeLocale([DWARFISH_NORTH]);
const events = [];
collectEvents(localization, Localization.UNKNOWN_LOCALIZATION, events);
// We expect to see a non-empty string returned and to see NO "missing
// localization" events.
expect(localization.resolve(HOW_ARE_YOU)).toBeTruthy();
expect(events.length).toBe(0);
});
it('will not fire when key is found in fallback', () => {
localization.insert(HALFLING_COMMON, HALFLING_COMMON_MAP);
localization.changeLocale([DWARFISH_NORTH]);
const events = [];
collectEvents(localization, Localization.UNKNOWN_LOCALIZATION, events);
// We expect to see a non-empty string returned and to see NO "missing
// localization" events.
expect(localization.resolve(HOW_ARE_YOU)).toBeTruthy();
expect(events.length).toBe(0);
});
it('fires when key is not found', () => {
localization.changeLocale([DWARFISH_NORTH]);
const events = [];
collectEvents(localization, Localization.UNKNOWN_LOCALIZATION, events);
// When nothing is found an empty string should be returned and we should
// see a "missing localization" event.
expect(localization.resolve(HOW_ARE_YOU)).toBe('');
expect(events.length).toBe(1);
});
// This test is similar to "missing from everything", but the difference
// here is that the entry can be found in a locale that is not part of
// search.
it('fires when key is not found in fallback, siblings, base, or preferred',
() => {
localization.insert(HALFLING_SHIRE, HALFLING_SHIRE_MAP);
localization.changeLocale([DWARFISH_NORTH]);
const events = [];
collectEvents(
localization, Localization.UNKNOWN_LOCALIZATION, events);
// When nothing is found an empty string should be returned and we
// should see a "missing localization" event.
expect(localization.resolve(HOW_ARE_YOU)).toBe('');
expect(events.length).toBe(1);
});
});
// The "missing localizations event" is fired when all preferred locales do
// not have the requested localization but a related locale does have the
// requested localization
describe('missing localizations event', () => {
it('fires when key/value is missing from preferred but found in fallback',
() => {
// Initialize the localization system so that we have an entry for
// "hello" in our fallback, but not in our preferred locale.
const localization = new Localization(HALFLING_COMMON);
localization.insert(HALFLING_COMMON, new Map([
['hello', 'Hallo! Hallo! And who may you be?'],
]));
localization.insert(ELVISH_WOODLAND, new Map());
const events = [];
collectEvents(
localization, Localization.MISSING_LOCALIZATIONS, events);
// Change locales should cause the missing localizations event to fire
// and report that we are missing an entry for "hello".
localization.changeLocale([ELVISH_WOODLAND]);
// We should be told that we are missing an entry for "hello" because
// it was in the fallback (HALFLING) but not our preferred language
// (EVLISH).
expect(events.length).toBe(1);
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
expect(events[0]['missing']).toEqual(['hello']);
});
it('fires when key/value is missing from preferred but found in base',
() => {
// Initialize the localization system so that we have an entry for
// "Best Wishes" in our base language, but not in our preferred
// locale.
const localization = new Localization(HALFLING_COMMON);
localization.insert(ELVISH_WOODLAND, new Map());
localization.insert(ELVISH, new Map([
['Best Wishes', 'Merin sa haryalye alasse'],
]));
const events = [];
collectEvents(
localization, Localization.MISSING_LOCALIZATIONS, events);
// Change locales should cause the missing localizations event to fire
// and report that we are missing an entry for "Best Wishes".
localization.changeLocale([ELVISH_WOODLAND]);
// We should be told that we are missing an entry for "Best Wishes"
// because it was in the base (ELVISH) but not our preferred language
// (EVLISH_WOODLAND).
expect(events.length).toBe(1);
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
expect(events[0]['missing']).toEqual(['Best Wishes']);
});
it('fires when key/value is missing from preferred but found in sibling',
() => {
// Initialize the localization system so that we have an entry for
// "Do you understand" in a language that shares a base language with
// our preferred locale, but not in our preferred locale.
const localization = new Localization(ELVISH_WOODLAND);
localization.insert(HALFLING_COMMON, new Map());
localization.insert(HALFLING_SHIRE, new Map([
['Do you understand', 'If you take my meaning.'],
]));
const events = [];
collectEvents(
localization, Localization.MISSING_LOCALIZATIONS, events);
// Change locales should cause the missing localizations event to fire
// and report that we are missing an entry for "Best Wishes".
localization.changeLocale([HALFLING_COMMON]);
// We should be told that we are missing an entry for "Do you
// understand" because it was in a sibling locale (HALFLING_SHIRE)
// but not our preferred language (HALFLING_COMMON).
expect(events.length).toBe(1);
expect(events[0]['locales']).toEqual([HALFLING_COMMON]);
expect(events[0]['missing']).toEqual(['Do you understand']);
});
it('fires when key/value is missing from some preferred languages',
() => {
// Initialize the localization system so that we have an entry for
// "may your forge burn bright" in our second preference but not our
// first.
const localization = new Localization(HALFLING_COMMON);
localization.insert(HALFLING_COMMON, new Map());
localization.insert(ELVISH_WOODLAND, new Map());
localization.insert(DWARFISH_NORTH, new Map([
['may your forge burn bright', 'tan menu selek lanun khun'],
]));
const events = [];
collectEvents(
localization, Localization.MISSING_LOCALIZATIONS, events);
// Changing locales should not fire the missing localization event
// because the localization for "may your forge burn bright" can
// be found in our secondary preferred language.
localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
// There should have no missing localization events.
expect(events.length).toBe(1);
expect(events[0]['locales']).toEqual(
[ELVISH_WOODLAND, DWARFISH_NORTH]);
expect(events[0]['missing']).toEqual(['may your forge burn bright']);
});
});
describe('locale changed event', () => {
it('fires when locale changes', () => {
const localization = new Localization(HALFLING_COMMON);
const events = [];
collectEvents(localization, Localization.LOCALE_CHANGED, events);
// We are going from nothing to something, we should see an event.
localization.changeLocale([DWARFISH_SOUTH]);
expect(events.length).toBe(1);
// We are changing from SOUTH to NORTH, we should see another event.
localization.changeLocale([DWARFISH_NORTH]);
expect(events.length).toBe(2);
});
it('fires when changing to the same locale', () => {
const localization = new Localization(HALFLING_COMMON);
const events = [];
collectEvents(localization, Localization.LOCALE_CHANGED, events);
// We are going from nothing to something, we should see an event.
localization.changeLocale([DWARFISH_SOUTH]);
expect(events.length).toBe(1);
// We are going to ask to go our current locale, even through
// conceptually this would be a no-op, the event should still fire.
localization.changeLocale([DWARFISH_SOUTH]);
expect(events.length).toBe(2);
});
});
/**
* Listen to |localization| for specific events and save them in |out|.
*
* @param {!shaka.ui.Localization} localization
* @param {string} eventName
* @param {!Array.<!shaka.util.FakeEvent>} out
*/
function collectEvents(localization, eventName, out) {
const onEvent = (event) => {
const fakeEvent = /** @type {!shaka.util.FakeEvent} */ (event);
out.push(fakeEvent);
};
localization.addEventListener(eventName, onEvent);
}
});