Skip to content

Commit

Permalink
feat(translation): add functionality to enable the use of translation…
Browse files Browse the repository at this point in the history
…s in Stark modules
  • Loading branch information
RobbyDeLaet committed May 4, 2018
1 parent 40f745b commit fbdfc25
Show file tree
Hide file tree
Showing 18 changed files with 900 additions and 520 deletions.
4 changes: 2 additions & 2 deletions .release-it.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"force": false,
"pkgFiles": ["package.json"],
"increment": "conventional:angular",
"beforeChangelogCommand": "npm run generate:changelog",
"changelogCommand": "npm run generate:changelog-recent",
"beforeChangelogCommand": "npm run generate:changelog",
"changelogCommand": "npm run generate:changelog-recent",
"buildCommand": false,
"safeBump": false,
"beforeChangelogCommand": false,
Expand Down
2 changes: 1 addition & 1 deletion packages/stark-build/config/ng-cli-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function validateAngularCLIConfig(jsonConfig) {
try {
const serializedConfig = JSON.parse(config.$$serialize("application/json"));
return serializedConfig;
} catch(error) {
} catch (error) {
return false;
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/stark-build/config/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ module.exports = function(options) {
sourceMap: true // TODO: apply based on tsConfig value?
}
};

const rootDir = buildUtils.getAngularCliAppConfig().root;

return {
Expand Down Expand Up @@ -359,8 +359,8 @@ module.exports = function(options) {
new CopyWebpackPlugin(
[
...buildUtils.getNbbAssetsConfig(),
...buildUtils.getApplicationAssetsConfig(),

...buildUtils.getApplicationAssetsConfig()
// TODO uncomment this when is part of Stark
// // Stark assets
// {
Expand Down
2 changes: 1 addition & 1 deletion packages/stark-core/.ng-cli-config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "stark-core"
},
Expand Down
4 changes: 4 additions & 0 deletions packages/stark-core/src/common/translations/locale.intf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface StarkLocale {
languageCode: string;
translations: Object;
}
113 changes: 113 additions & 0 deletions packages/stark-core/src/common/translations/translations.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { inject, TestBed } from "@angular/core/testing";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { mergeTranslations } from "./translations";
import { StarkLocale } from "./locale.intf";

describe("Translations: mergeTranslations", () => {
const translateModule: TranslateModule = TranslateModule.forRoot();
let translateService: TranslateService;

const moduleTranslationsEn: any = {
STARK: {
TEST: {
TEXT1: "Text1 from module",
TEXT2: "Text2 from module"
}
}
};

const appTranslationsEn: any = {
STARK: {
TEST: {
TEXT2: "Text2 from app",
TEXT3: "Text3 from app"
}
}
};

const appTranslationsDe: any = {
STARK: {
TEST: {
TEXT1: "Text1 aus der app",
TEXT2: "Text2 aus der app",
TEXT3: "Text3 aus der app"
}
}
};

beforeEach(() => {
TestBed.configureTestingModule({
imports: [translateModule]
});
});

// Inject module dependencies
beforeEach(
inject([TranslateService], (_translateService: TranslateService) => {
translateService = _translateService;
translateService.addLangs(["en", "fr", "nl", "de"]);
translateService.setDefaultLang("en");
})
);

describe("mergeTranslations", () => {
it("should return the merged translations from common, module and app", () => {
// First the module is loaded
const english: StarkLocale = { languageCode: "en", translations: moduleTranslationsEn };
mergeTranslations(translateService, english);

// then the app is loaded
translateService.setTranslation("en", appTranslationsEn, true);
translateService.use("en");

const text1: string = translateService.instant("STARK.TEST.TEXT1");
const text2: string = translateService.instant("STARK.TEST.TEXT2");
const text3: string = translateService.instant("STARK.TEST.TEXT3");
const ascending: string = translateService.instant("STARK.SORTING.ASC");

expect(text1).toBe("Text1 from module");
expect(text2).toBe("Text2 from app"); // the app has the upper hand
expect(text3).toBe("Text3 from app");
expect(ascending).toBe("Ascending"); // Common translations
});

it("should return the merged translations from common, module and app when lazy loading modules", () => {
// First the app is loaded
translateService.setTranslation("en", appTranslationsEn, true);
translateService.use("en");

// Then the module is lazy loaded
const english: StarkLocale = { languageCode: "en", translations: moduleTranslationsEn };
mergeTranslations(translateService, english);

const text1: string = translateService.instant("STARK.TEST.TEXT1");
const text2: string = translateService.instant("STARK.TEST.TEXT2");
const text3: string = translateService.instant("STARK.TEST.TEXT3");
const ascending: string = translateService.instant("STARK.SORTING.ASC");

expect(text1).toBe("Text1 from module");
expect(text2).toBe("Text2 from app"); // the app has the upper hand
expect(text3).toBe("Text3 from app");
expect(ascending).toBe("Ascending"); // Common translations
});

it("should return the merged translations, where the app has an extra language", () => {
translateService.setTranslation("en", appTranslationsEn, true);
translateService.setTranslation("de", appTranslationsDe, true);
translateService.use("de");

const english: StarkLocale = { languageCode: "en", translations: moduleTranslationsEn };
mergeTranslations(translateService, english);

const text1: string = translateService.instant("STARK.TEST.TEXT1");
const text2: string = translateService.instant("STARK.TEST.TEXT2");
const text3: string = translateService.instant("STARK.TEST.TEXT3");
const ascending: string = translateService.instant("STARK.SORTING.ASC");

expect(text1).toBe("Text1 aus der app");
expect(text2).toBe("Text2 aus der app");
expect(text3).toBe("Text3 aus der app");
expect(ascending).toBe("Ascending"); // missing in the app => translated from the default language 'en'
});
});
});
53 changes: 53 additions & 0 deletions packages/stark-core/src/common/translations/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { TranslateService } from "@ngx-translate/core";
const _cloneDeep: Function = require("lodash/cloneDeep");

import { StarkLocale } from "./locale.intf";
import { translationsEn } from "./translations/en";
import { translationsFr } from "./translations/fr";
import { translationsNl } from "./translations/nl";

/**
* This function can be used by Stark modules to merge their translations into existing translations,
* without losing any existing translations.
*
* There are three 'levels' that can provide translations:
* 1. Common: for the Stark framework, they are stored in the 'translations' folder in the current folder.
* 2. Module: each module contains it's own translations, to make Stark fully modular.
* 3. App: the translations provided by the client application that implements the Stark framework.
*
* These levels have a hierarchy, where the common translations have to lowest priority and the translations
* of the client application have the highest priority. This means that if one or more levels have a
* translation with the same key, the translation with the highest priority 'wins'.
*
* Functionality:
* First the existing translations are stored in a variable 'translations'.
* The function then iterates over the 'args' parameters.
* For each language, the following steps are processed:
* 1. The translateService is cleaned (shouldMerge = false)
* 2. The 'common' translations are added in the translateService
* 3. The 'module' translations are added in the translateService, replacing any existing translations
* 4. The 'stored' translations are added in the translateService, replacing any existing translations
*
* @param translateService
* Description: a reference to the translateService instance
* @param args
* Description: a list of StarkLocales that contain the translations for a specific language
*
* @example:
* mergeTranslations(this.translateService, english, french, dutch);
*/
export function mergeTranslations(translateService: TranslateService, ...args: StarkLocale[]): void {
const locales: StarkLocale[] = [...args];
const translations: object = _cloneDeep(translateService.translations);

const commonTranslations: any[] = [];
commonTranslations["en"] = translationsEn;
commonTranslations["fr"] = translationsFr;
commonTranslations["nl"] = translationsNl;

locales.forEach((starkLocale: StarkLocale) => {
translateService.setTranslation(starkLocale.languageCode, commonTranslations[starkLocale.languageCode], false);
translateService.setTranslation(starkLocale.languageCode, starkLocale.translations, true);
translateService.setTranslation(starkLocale.languageCode, translations[starkLocale.languageCode], true);
});
}
14 changes: 14 additions & 0 deletions packages/stark-core/src/common/translations/translations/en.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const translationsEn: object = {
STARK: {
LANGUAGES: {
EN: "English",
FR: "Français",
NL: "Nederlands",
DE: "Deutsch"
},
SORTING: {
ASC: "Ascending",
DESC: "Descending"
}
}
};
14 changes: 14 additions & 0 deletions packages/stark-core/src/common/translations/translations/fr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const translationsFr: object = {
STARK: {
LANGUAGES: {
EN: "English",
FR: "Français",
NL: "Nederlands",
DE: "Deutsch"
},
SORTING: {
ASC: "Ascendant",
DESC: "Descendant"
}
}
};
14 changes: 14 additions & 0 deletions packages/stark-core/src/common/translations/translations/nl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const translationsNl: object = {
STARK: {
LANGUAGES: {
EN: "English",
FR: "Français",
NL: "Nederlands",
DE: "Deutsch"
},
SORTING: {
ASC: "Oplopend",
DESC: "Aflopend"
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class StarkSettingsEffects {
public setPreferredLanguage$(): Observable<void> {
return this.actions$.pipe(
ofType<SetPreferredLanguage>(StarkSettingsActionTypes.SET_PREFERRED_LANGUAGE),
map((action: SetPreferredLanguage) =>this.sessionService.setCurrentLanguage(action.language))
map((action: SetPreferredLanguage) => this.sessionService.setCurrentLanguage(action.language))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export class StarkSettingsServiceImpl implements StarkSettingsService {
this.store
.pipe(
select(selectStarkUser),
filter((user?: StarkUser) => typeof user !== "undefined" && typeof user.language !== "undefined"))
filter((user?: StarkUser) => typeof user !== "undefined" && typeof user.language !== "undefined")
)
.subscribe((user?: StarkUser) => {
if (
(<StarkUser>user).language !== null &&
Expand Down
8 changes: 4 additions & 4 deletions packages/stark-testing/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@ const root = path.join.bind(path, _root);
function getAngularCliAppConfig() {
const applicationAngularCliConfigPath = root(".angular-cli.json");
const packageAngularCliConfigPath = root(".ng-cli-config.json");

let angularCliConfigPath;

if (fs.existsSync(applicationAngularCliConfigPath)) {
angularCliConfigPath = applicationAngularCliConfigPath;
} else if (fs.existsSync(packageAngularCliConfigPath)) {
angularCliConfigPath = packageAngularCliConfigPath;
} else {
throw new Error(".angular-cli.json is not present. Please add this at the root your project because stark-build needs this.")
throw new Error(".angular-cli.json is not present. Please add this at the root your project because stark-build needs this.");
}

const angularCliConfig = require(angularCliConfigPath);
if (angularCliConfig["apps"] && angularCliConfig["apps"][0]) {
return angularCliConfig["apps"][0];
} else {
throw new Error("Angular-cli config apps is wrong. Please adapt it to follow Angular CLI way.")
throw new Error("Angular-cli config apps is wrong. Please adapt it to follow Angular CLI way.");
}
}

Expand Down

0 comments on commit fbdfc25

Please sign in to comment.