meta object
This commit is contained in:
@@ -34,8 +34,7 @@ The frontend has an API formatter that takes the OpenAPI schema file and convert
|
||||
|
||||
### Caveats
|
||||
|
||||
1. There is currently no way to add translations. All texts in the UI are expected to be written in Umbraco’s default language of English.
|
||||
2. The backoffice can be run and tested against a real Umbraco instance by cloning down the `v14/dev` branch, but there are no guarantees about how well it works yet.
|
||||
1. The backoffice can be run and tested against a real Umbraco instance by cloning down the `v14/dev` branch, but there are no guarantees about how well it works yet.
|
||||
|
||||
**Current schema for API:**
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import '../src/libs/controller-api/controller-host-initializer.element.ts';
|
||||
import '../src/packages/core/components';
|
||||
|
||||
import { manifests as documentManifests } from '../src/packages/documents';
|
||||
import { manifests as translationManifests } from '../src/packages/core/localization/manifests';
|
||||
import { manifests as localizationManifests } from '../src/packages/core/localization/manifests';
|
||||
|
||||
// MSW
|
||||
startMockServiceWorker({ serviceWorker: { url: (import.meta.env.VITE_BASE_PATH ?? '/') + 'mockServiceWorker.js' } });
|
||||
@@ -39,7 +39,7 @@ class UmbStoryBookElement extends UmbLitElement {
|
||||
this._registerExtensions(documentManifests);
|
||||
this.provideContext(UMB_MODAL_CONTEXT_TOKEN, new UmbModalManagerContext(this));
|
||||
|
||||
this._registerExtensions(translationManifests);
|
||||
this._registerExtensions(localizationManifests);
|
||||
umbLocalizationRegistry.loadLanguage('en-us'); // register default language
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { aTimeout, elementUpdated, expect, fixture, html } from '@open-wc/testing';
|
||||
import { DefaultTranslationSet, TranslationSet, registerTranslation, translations } from './manager.js';
|
||||
import { DefaultLocalizationSet, LocalizationSet, registerLocalization, localizations } from './manager.js';
|
||||
import { UmbLocalizeController } from './localize.controller.js';
|
||||
import { LitElement, customElement, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
|
||||
@@ -10,7 +10,7 @@ class UmbLocalizeControllerHostElement extends UmbElementMixin(LitElement) {
|
||||
@property() lang = 'en-us';
|
||||
}
|
||||
|
||||
interface TestTranslation extends TranslationSet {
|
||||
interface TestLocalization extends LocalizationSet {
|
||||
close: string;
|
||||
logout: string;
|
||||
withInlineToken: any;
|
||||
@@ -19,8 +19,8 @@ interface TestTranslation extends TranslationSet {
|
||||
numUsersSelected: (count: number) => string;
|
||||
}
|
||||
|
||||
//#region Translations
|
||||
const english: TestTranslation = {
|
||||
//#region Localizations
|
||||
const english: TestLocalization = {
|
||||
$code: 'en-us',
|
||||
$dir: 'ltr',
|
||||
close: 'Close',
|
||||
@@ -35,20 +35,20 @@ const english: TestTranslation = {
|
||||
},
|
||||
};
|
||||
|
||||
const englishOverride: DefaultTranslationSet = {
|
||||
const englishOverride: DefaultLocalizationSet = {
|
||||
$code: 'en-us',
|
||||
$dir: 'ltr',
|
||||
close: 'Close 2',
|
||||
};
|
||||
|
||||
const danish: DefaultTranslationSet = {
|
||||
const danish: DefaultLocalizationSet = {
|
||||
$code: 'da',
|
||||
$dir: 'ltr',
|
||||
close: 'Luk',
|
||||
notOnRegional: 'Not on regional',
|
||||
};
|
||||
|
||||
const danishRegional: DefaultTranslationSet = {
|
||||
const danishRegional: DefaultLocalizationSet = {
|
||||
$code: 'da-dk',
|
||||
$dir: 'ltr',
|
||||
close: 'Luk',
|
||||
@@ -56,10 +56,10 @@ const danishRegional: DefaultTranslationSet = {
|
||||
//#endregion
|
||||
|
||||
describe('UmbLocalizeController', () => {
|
||||
let controller: UmbLocalizeController<TestTranslation>;
|
||||
let controller: UmbLocalizeController<TestLocalization>;
|
||||
|
||||
beforeEach(async () => {
|
||||
registerTranslation(english, danish, danishRegional);
|
||||
registerLocalization(english, danish, danishRegional);
|
||||
document.documentElement.lang = english.$code;
|
||||
document.documentElement.dir = english.$dir;
|
||||
await aTimeout(0);
|
||||
@@ -76,7 +76,7 @@ describe('UmbLocalizeController', () => {
|
||||
|
||||
afterEach(() => {
|
||||
controller.destroy();
|
||||
translations.clear();
|
||||
localizations.clear();
|
||||
});
|
||||
|
||||
it('should have a default language', () => {
|
||||
@@ -131,7 +131,7 @@ describe('UmbLocalizeController', () => {
|
||||
|
||||
it('should override a term if new translation is registered', () => {
|
||||
// Let the registry load the new extension
|
||||
registerTranslation(englishOverride);
|
||||
registerLocalization(englishOverride);
|
||||
|
||||
expect(controller.term('close')).to.equal('Close 2');
|
||||
});
|
||||
@@ -174,7 +174,7 @@ describe('UmbLocalizeController', () => {
|
||||
|
||||
it('should return a date with a custom format', () => {
|
||||
expect(controller.date(new Date(2020, 11, 31), { month: 'long', day: '2-digit', year: 'numeric' })).to.equal(
|
||||
'December 31, 2020'
|
||||
'December 31, 2020',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -198,7 +198,7 @@ describe('UmbLocalizeController', () => {
|
||||
|
||||
it('should return a number with a custom format', () => {
|
||||
expect(controller.number(123456.789, { minimumFractionDigits: 2, maximumFractionDigits: 2 })).to.equal(
|
||||
'123,456.79'
|
||||
'123,456.79',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,14 +12,14 @@ The above copyright notice and this permission notice shall be included in all c
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import {
|
||||
DefaultTranslationSet,
|
||||
DefaultLocalizationSet,
|
||||
FunctionParams,
|
||||
TranslationSet,
|
||||
LocalizationSet,
|
||||
connectedElements,
|
||||
documentDirection,
|
||||
documentLanguage,
|
||||
fallback,
|
||||
translations,
|
||||
localizations,
|
||||
} from './manager.js';
|
||||
import { UmbController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
@@ -42,7 +42,7 @@ const LocalizeControllerAlias = Symbol();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class UmbLocalizeController<TranslationType extends TranslationSet = DefaultTranslationSet>
|
||||
export class UmbLocalizeController<LocalizationType extends LocalizationSet = DefaultLocalizationSet>
|
||||
implements UmbController
|
||||
{
|
||||
#host;
|
||||
@@ -88,19 +88,19 @@ export class UmbLocalizeController<TranslationType extends TranslationSet = Defa
|
||||
return `${this.#hostEl.lang || documentLanguage}`.toLowerCase();
|
||||
}
|
||||
|
||||
private getTranslationData(lang: string) {
|
||||
private getLocalizationData(lang: string) {
|
||||
const locale = new Intl.Locale(lang);
|
||||
const language = locale?.language.toLowerCase();
|
||||
const region = locale?.region?.toLowerCase() ?? '';
|
||||
const primary = <TranslationType>translations.get(`${language}-${region}`);
|
||||
const secondary = <TranslationType>translations.get(language);
|
||||
const primary = <LocalizationType>localizations.get(`${language}-${region}`);
|
||||
const secondary = <LocalizationType>localizations.get(language);
|
||||
|
||||
return { locale, language, region, primary, secondary };
|
||||
}
|
||||
|
||||
/** Outputs a translated term. */
|
||||
term<K extends keyof TranslationType>(key: K, ...args: FunctionParams<TranslationType[K]>): string {
|
||||
const { primary, secondary } = this.getTranslationData(this.lang());
|
||||
term<K extends keyof LocalizationType>(key: K, ...args: FunctionParams<LocalizationType[K]>): string {
|
||||
const { primary, secondary } = this.getLocalizationData(this.lang());
|
||||
let term: any;
|
||||
|
||||
// Look for a matching term using regionCode, code, then the fallback
|
||||
@@ -108,8 +108,8 @@ export class UmbLocalizeController<TranslationType extends TranslationSet = Defa
|
||||
term = primary[key];
|
||||
} else if (secondary && secondary[key]) {
|
||||
term = secondary[key];
|
||||
} else if (fallback && fallback[key as keyof TranslationSet]) {
|
||||
term = fallback[key as keyof TranslationSet];
|
||||
} else if (fallback && fallback[key as keyof LocalizationSet]) {
|
||||
term = fallback[key as keyof LocalizationSet];
|
||||
} else {
|
||||
return String(key);
|
||||
}
|
||||
|
||||
@@ -16,21 +16,21 @@ import type { LitElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
export type FunctionParams<T> = T extends (...args: infer U) => string ? U : [];
|
||||
|
||||
export interface TranslationSet {
|
||||
export interface LocalizationSet {
|
||||
$code: string; // e.g. en, en-GB
|
||||
$dir: 'ltr' | 'rtl';
|
||||
}
|
||||
|
||||
export interface DefaultTranslationSet extends TranslationSet {
|
||||
export interface DefaultLocalizationSet extends LocalizationSet {
|
||||
[key: string]: UmbLocalizationEntry;
|
||||
}
|
||||
|
||||
export const connectedElements = new Set<HTMLElement>();
|
||||
const documentElementObserver = new MutationObserver(update);
|
||||
export const translations: Map<string, TranslationSet> = new Map();
|
||||
export const localizations: Map<string, LocalizationSet> = new Map();
|
||||
export let documentDirection = document.documentElement.dir || 'ltr';
|
||||
export let documentLanguage = document.documentElement.lang || navigator.language;
|
||||
export let fallback: TranslationSet;
|
||||
export let fallback: LocalizationSet;
|
||||
|
||||
// Watch for changes on <html lang>
|
||||
documentElementObserver.observe(document.documentElement, {
|
||||
@@ -39,15 +39,15 @@ documentElementObserver.observe(document.documentElement, {
|
||||
});
|
||||
|
||||
/** Registers one or more translations */
|
||||
export function registerTranslation(...translation: TranslationSet[]) {
|
||||
export function registerLocalization(...translation: LocalizationSet[]) {
|
||||
translation.map((t) => {
|
||||
const code = t.$code.toLowerCase();
|
||||
|
||||
if (translations.has(code)) {
|
||||
if (localizations.has(code)) {
|
||||
// Merge translations that share the same language code
|
||||
translations.set(code, { ...translations.get(code), ...t });
|
||||
localizations.set(code, { ...localizations.get(code), ...t });
|
||||
} else {
|
||||
translations.set(code, t);
|
||||
localizations.set(code, t);
|
||||
}
|
||||
|
||||
// The first translation that's registered is the fallback
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface MetaLocalization {
|
||||
culture: string;
|
||||
|
||||
/**
|
||||
* @summary The direction of the translations (left-to-right or right-to-left).
|
||||
* @summary The direction of the localizations (left-to-right or right-to-left).
|
||||
* @description
|
||||
* The value is used to describe the direction of the translations according to the extension system
|
||||
* and it will be set as the `dir` attribute on the `<html>` element. It defaults to `ltr`.
|
||||
@@ -31,7 +31,7 @@ export interface MetaLocalization {
|
||||
direction?: 'ltr' | 'rtl';
|
||||
|
||||
/**
|
||||
* The translations.
|
||||
* The localizations.
|
||||
* @example
|
||||
* {
|
||||
* "general": {
|
||||
@@ -40,5 +40,5 @@ export interface MetaLocalization {
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
translations?: UmbLocalizationDictionary;
|
||||
localizations?: UmbLocalizationDictionary;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const english = {
|
||||
name: 'Test English',
|
||||
meta: {
|
||||
culture: 'en',
|
||||
translations: {
|
||||
localizations: {
|
||||
general: {
|
||||
close: 'Close',
|
||||
logout: 'Log out',
|
||||
@@ -33,7 +33,7 @@ const danish = {
|
||||
name: 'Test Danish',
|
||||
meta: {
|
||||
culture: 'da',
|
||||
translations: {
|
||||
localizations: {
|
||||
general: {
|
||||
close: 'Luk',
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ const english: ManifestLocalization = {
|
||||
meta: {
|
||||
culture: 'en-us',
|
||||
direction: 'ltr',
|
||||
translations: {
|
||||
localizations: {
|
||||
general: {
|
||||
close: 'Close',
|
||||
logout: 'Log out',
|
||||
@@ -32,7 +32,7 @@ const englishOverride: ManifestLocalization = {
|
||||
name: 'Test English',
|
||||
meta: {
|
||||
culture: 'en-us',
|
||||
translations: {
|
||||
localizations: {
|
||||
general: {
|
||||
close: 'Close 2',
|
||||
},
|
||||
@@ -46,7 +46,7 @@ const danish: ManifestLocalization = {
|
||||
name: 'Test Danish',
|
||||
meta: {
|
||||
culture: 'da',
|
||||
translations: {
|
||||
localizations: {
|
||||
general: {
|
||||
close: 'Luk',
|
||||
notOnRegional: 'Not on regional',
|
||||
@@ -61,7 +61,7 @@ const danishRegional: ManifestLocalization = {
|
||||
name: 'Test Danish (Denmark)',
|
||||
meta: {
|
||||
culture: 'da-dk',
|
||||
translations: {
|
||||
localizations: {
|
||||
general: {
|
||||
close: 'Luk',
|
||||
},
|
||||
@@ -84,7 +84,7 @@ describe('UmbLocalizeController', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
registry.translations.clear();
|
||||
registry.localizations.clear();
|
||||
});
|
||||
|
||||
it('should set the document language and direction', async () => {
|
||||
@@ -93,9 +93,9 @@ describe('UmbLocalizeController', () => {
|
||||
});
|
||||
|
||||
it('should load translations for the current language', async () => {
|
||||
expect(registry.translations.has(english.meta.culture)).to.be.true;
|
||||
expect(registry.localizations.has(english.meta.culture)).to.be.true;
|
||||
|
||||
const current = registry.translations.get(english.meta.culture);
|
||||
const current = registry.localizations.get(english.meta.culture);
|
||||
expect(current).to.have.property('general_close', 'Close'); // Also tests that the translation is flattened.
|
||||
expect(current).to.have.property('general_logout', 'Log out');
|
||||
});
|
||||
@@ -105,7 +105,7 @@ describe('UmbLocalizeController', () => {
|
||||
|
||||
await aTimeout(0);
|
||||
|
||||
const current = registry.translations.get(english.meta.culture);
|
||||
const current = registry.localizations.get(english.meta.culture);
|
||||
expect(current).to.have.property('general_close', 'Close 2');
|
||||
expect(current).to.have.property('general_logout', 'Log out');
|
||||
});
|
||||
@@ -116,10 +116,10 @@ describe('UmbLocalizeController', () => {
|
||||
await aTimeout(0);
|
||||
|
||||
// Check that the new language is loaded.
|
||||
expect(registry.translations.has(danish.meta.culture)).to.be.true;
|
||||
expect(registry.localizations.has(danish.meta.culture)).to.be.true;
|
||||
|
||||
// Check that the new language has the correct translations.
|
||||
const current = registry.translations.get(danish.meta.culture);
|
||||
const current = registry.localizations.get(danish.meta.culture);
|
||||
expect(current).to.have.property('general_close', 'Luk');
|
||||
});
|
||||
|
||||
@@ -129,7 +129,7 @@ describe('UmbLocalizeController', () => {
|
||||
await aTimeout(0);
|
||||
|
||||
// Check that both the regional and the base language is loaded.
|
||||
expect(registry.translations.has(danishRegional.meta.culture), 'expected "da-dk" to be present').to.be.true;
|
||||
expect(registry.translations.has(danish.meta.culture), 'expected "da" to be present').to.be.true;
|
||||
expect(registry.localizations.has(danishRegional.meta.culture), 'expected "da-dk" to be present').to.be.true;
|
||||
expect(registry.localizations.has(danish.meta.culture), 'expected "da" to be present').to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
UmbLocalizationDictionary,
|
||||
UmbLocalizationFlatDictionary,
|
||||
TranslationSet,
|
||||
registerTranslation,
|
||||
translations,
|
||||
LocalizationSet,
|
||||
registerLocalization,
|
||||
localizations,
|
||||
} from '@umbraco-cms/backoffice/localization-api';
|
||||
import { hasDefaultExport, loadExtension } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { UmbBackofficeExtensionRegistry, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
@@ -21,8 +21,8 @@ export class UmbLocalizationRegistry {
|
||||
/**
|
||||
* Get the current registered translations.
|
||||
*/
|
||||
get translations() {
|
||||
return translations;
|
||||
get localizations() {
|
||||
return localizations;
|
||||
}
|
||||
|
||||
get isDefaultLoaded() {
|
||||
@@ -57,8 +57,8 @@ export class UmbLocalizationRegistry {
|
||||
const innerDictionary: UmbLocalizationFlatDictionary = {};
|
||||
|
||||
// If extension contains a dictionary, add it to the inner dictionary.
|
||||
if (extension.meta.translations) {
|
||||
for (const [dictionaryName, dictionary] of Object.entries(extension.meta.translations)) {
|
||||
if (extension.meta.localizations) {
|
||||
for (const [dictionaryName, dictionary] of Object.entries(extension.meta.localizations)) {
|
||||
this.#addOrUpdateDictionary(innerDictionary, dictionaryName, dictionary);
|
||||
}
|
||||
}
|
||||
@@ -77,12 +77,12 @@ export class UmbLocalizationRegistry {
|
||||
$code: extension.meta.culture.toLowerCase(),
|
||||
$dir: extension.meta.direction ?? 'ltr',
|
||||
...innerDictionary,
|
||||
} satisfies TranslationSet;
|
||||
} satisfies LocalizationSet;
|
||||
}),
|
||||
);
|
||||
|
||||
if (translations.length) {
|
||||
registerTranslation(...translations);
|
||||
registerLocalization(...translations);
|
||||
|
||||
// Set the document language
|
||||
const newLang = locale.baseName.toLowerCase();
|
||||
|
||||
@@ -34,10 +34,10 @@ const menuSectionSidebarApp: ManifestTypes = {
|
||||
const dashboards: Array<ManifestDashboard> = [
|
||||
{
|
||||
type: 'dashboard',
|
||||
alias: 'Umb.Dashboard.TranslationDictionary',
|
||||
name: 'Dictionary Translation Dashboard',
|
||||
alias: 'Umb.Dashboard.LocalizationDictionary',
|
||||
name: 'Dictionary localization Dashboard',
|
||||
elementName: 'umb-dashboard-translation-dictionary',
|
||||
loader: () => import('./dashboards/dictionary/dashboard-translation-dictionary.element.js'),
|
||||
loader: () => import('./dashboards/dictionary/dashboard-localization-dictionary.element.js'),
|
||||
meta: {
|
||||
label: 'Dictionary overview',
|
||||
pathname: '',
|
||||
|
||||
Reference in New Issue
Block a user