Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/default ts translations #38

Merged
merged 13 commits into from May 7, 2019
@@ -4,6 +4,7 @@ version 0.5.0
- adds option to UniferConfiguration that allows to change the way how request extractors are found.
- adds option to pass component's defaultConfiguration a function that returns a new configuration object
- passes list of components to except from autobind to inversify-components
- Loads all TypeScript, JavaScript and JSON files from locales directory, if i18next backend is not configured. `.ts` files shadow `.js` files, `.js` files shadow `.json` files. The folder/file structure is resembled in the final locales object, while the filenames are camelcased, i.e. a folder `main-state` will be a key `mainState`, which will contain all content from that folder. If LocalesLoader finds an ES6 default export or an export the same name as the camelcased file name, only this is taken from the file. In any other case, everything is exported. Files and folders of the same name are merged. An example can be found [here](https://gist.github.com/baflo/b88e636948b6b57c3f07af2672088961).

version 0.4.1
- fixes typing of StayInContextCallback and ClearContextCallback

Some generated files are not rendered by default. Learn more.

@@ -47,6 +47,7 @@
"lodash": "^4.17.11",
"redis": "2.8.0",
"reflect-metadata": "^0.1.12",
"require-directory": "^2.1.1",
"resolve": "^1.8.1",
"rxjs": "^5.5.11"
},
@@ -17,10 +17,12 @@ const configuration: AssistantJSConfiguration = {
"core:i18n": {
// This is basically the i18next configuration. Check out https://www.i18next.com/ for more information!
i18nextAdditionalConfiguration: {
// This entry is needed and tells i18next where to find your language files.
backend: {
loadPath: process.cwd() + "/config/locales/{{lng}}/{{ns}}.json",
},
// Translation resources are automatically loaded from path given in `UnifierConfiguration#utterancePath`, but may be overridden here.
// resources: {}
// Alternatively you can give a path to a folder with JSON files that can be loaded by i18next
// backend: {
// loadPath: process.cwd() + "/config/locales/{{lng}}/{{ns}}.json",
// },
lngs: ["en"],
fallbackLng: "en",
// If you encouter problems with i18next, change this to true
@@ -1,8 +1,11 @@
import * as path from "path";

import { TEMPORARY_INTERPOLATION_END, TEMPORARY_INTERPOLATION_START } from "../../../src/components/i18n/interpolation-resolver";
import { arraySplitter } from "../../../src/components/i18n/plugins/array-returns-sample.plugin";
import { I18nextWrapper } from "../../../src/components/i18n/wrapper";
import { injectionNames } from "../../../src/injection-names";
import { configureI18nLocale } from "../../support/util/i18n-configuration";
import { configureUnifier } from "../../support/util/unifier-configuration";
import { ThisContext } from "../../this-context";

interface CurrentThisContext extends ThisContext {
@@ -56,3 +59,41 @@ describe("I18nWrapper", function() {
});
});
});

describe("I18nWrapper loading Typescript files", function(this: CurrentThisContext) {
const expectedTranslations = ["hello my name", "hi my name", "welcome my name"];

beforeEach(function(this: CurrentThisContext) {
this.specHelper.prepareSpec(this.defaultSpecOptions);
// Remove emitting of warnings
this.specHelper.bindSpecLogger("error");

configureI18nLocale(this.assistantJs.container, false);
configureUnifier(this.assistantJs.container, path.join(__dirname, "../../support/mocks/i18n-ts/locale/"));
this.wrapper = this.inversify.get("core:i18n:wrapper");
});

describe("translation function", function() {
it("returns one of many options", function(this: CurrentThisContext) {
expect(expectedTranslations).toContain(this.wrapper.instance.t("templateSyntaxSmall", { name: "my name" }));
});
});

describe("loading behaviour", function() {
it("load from file that has the same name as a directory", function() {
expect(this.wrapper.instance.store.data.de.translation.mySpecificKeys.keyOne).toBe("keyOneResult");
});

it("loads default only if exists", function() {
expect(this.wrapper.instance.store.data.de.translation.defaultExport.test).toBe("default");
});

it("loads export with camelcase filename if no default exists", function() {
expect(this.wrapper.instance.store.data.de.translation.templateSyntaxSmall[0]).toBe("{hello|hi} {{name}}");
});

it("loads all exports if neither default nor one equal to camelcase filename exists", function() {
expect(this.wrapper.instance.store.data.de.translation.mainState.testIntent.embedded.test).toBe("very-specific-without-extractor");
});
});
});
@@ -2,14 +2,13 @@ import * as fs from "fs";
import { Component, getMetaInjectionName } from "inversify-components";
import { resolve } from "path";
import { LocalesLoader, UnifierConfiguration } from "../../../src/assistant-source";
import { Configuration } from "../../../src/components/unifier/private-interfaces";
import { injectionNames } from "../../../src/injection-names";
import { ThisContext } from "../../this-context";

// tslint:disable no-var-requires
const deUtterances = require("../../support/mocks/i18n/locale/de/utterances.json");
const enUtterances = require("../../support/mocks/i18n/locale/en/utterances.js");
const deEntities = require("../../support/mocks/i18n/locale/de/entities.json");
const enUtterances = require("../../support/mocks/i18n/locale/en/utterances.ts");
const { default: deEntities } = require("../../support/mocks/i18n/locale/de/entities.ts");
const enEntities = require("../../support/mocks/i18n/locale/en/entities.js");
// tslint:enable no-var-requires

@@ -102,7 +101,7 @@ describe("LocalesLoader", function() {
});
});

it("prioritizes JS files over JSON files", function(this: CurrentThisContext) {
it("prioritizes JS files over JSON and TS over JS and JSON files", function(this: CurrentThisContext) {
const entities = this.localesLoader.getCustomEntities();
const utterances = this.localesLoader.getUtteranceTemplates();

@@ -121,7 +120,7 @@ describe("LocalesLoader", function() {
});
});

describe("getUtteranceTemplates", function() {
describe("#getUtteranceTemplates", function() {
it("loads all utterance templates of all languages from file system", function(this: CurrentThisContext) {
const utterances = this.localesLoader.getUtteranceTemplates();

@@ -133,7 +132,7 @@ describe("LocalesLoader", function() {
});
});

describe("getCustomEntities", function() {
describe("#getCustomEntities", function() {
it("loads all custom entities of all languages from file system", function(this: CurrentThisContext) {
const entities = this.localesLoader.getCustomEntities();

@@ -0,0 +1,56 @@
export default {
mySpecificKeys: {
keyOne: "keyOneResult",
},
multiple: ["a", "b"],
var: "a{{var}}",
multipleVars: "a{{firstVar}}b{{secondVar}}c{{thirdVar}}",
deviceDependentState: {
ExtractorComponent: {
device1: "state-platform-device-specific",
},
},
root: {
testIntent: {
embedded: {
platformDependent: {
ExtractorComponent: "platform-specific-embedded",
},
platformIndependent: "platform-independent-root-embedded",
},
withoutExtractor: "root-without-extractor",
deviceDependent: {
ExtractorComponent: {
device1: "root-intent-platform-device-specific",
},
},
},
secondPlatformSpecificIntent: {
ExtractorComponent: "root-platform-specific-intent",
},
yesGenericIntent: "root-yes",
rootKey: {
ExtractorComponent: "platform-specific-root-only",
},
ExtractorComponent: "root-only-platform-given",
},
noIntentState: "stateOnly",
templateSyntax: ["{Can|May} I help you, {{var}}?", "Would you like me to help you?"],
filter: {
stateA: {
intentA: "FilterAState - filterTestAIntent",
intentB: "FilterAState - filterTestBIntent",
intentC: "FilterAState - filterTestCIntent",
intentD: "FilterAState - filterTestDIntent",
},
stateB: {
intentA: "FilterBState - filterTestAIntent",
intentB: "FilterBState - filterTestBIntent",
},
stateC: {
intentA: "FilterCState - filterTestAIntent",
intentB: "FilterCState - filterTestBIntent",
},
},
noInterpolation: "no interpolation",
};
@@ -0,0 +1,2 @@
export const someState = { some: "state" };
export default { test: "default" };
@@ -0,0 +1,32 @@
export const testIntent = {
embedded: {
test: "very-specific-without-extractor",
platformDependent: {
ExtractorComponent: "platform-specific-sub-key",
},
},
};

export const deviceDependentIntent = {
embeddedKeyOuter: {
embeddedKeyInner: {
ExtractorComponent: {
device1: "device-specific-sub-key",
},
},
},
};

export const yesGenericIntent = "yes";

export const platformDependent = {
ExtractorComponent: "platform-specific-embedded-state-only",
};

export const platformIndependent = "platform-independent-main-state";

export const ExtractorComponent = "platform-specific-main-state-only";

export const platformSpecificIntent = {
ExtractorComponent: "platform-specific-intent",
};
@@ -0,0 +1 @@
export const templateSyntaxSmall = ["{hello|hi} {{name}}", "welcome {{name}}"];
@@ -1,16 +1,3 @@
{
"color": [
{
"value": "grün",
"synonyms": ["waldgrün", "salbei", "oliv", "limone", "jade", "minz"]
},
{
"value": "rot",
"synonyms": ["scharlach", "lachs", "karmin", "ziegel", "rubin", "blut"]
},
{
"value": "gelb",
"synonyms": ["kanariengelb", "flachs", "mais", "honig", "bernstein", "blond"]
}
]
}
"this should be shadowed by entities.ts": true
}
@@ -0,0 +1,16 @@
export default {
color: [
{
value: "grün",
synonyms: ["waldgrün", "salbei", "oliv", "limone", "jade", "minz"],
},
{
value: "rot",
synonyms: ["scharlach", "lachs", "karmin", "ziegel", "rubin", "blut"],
},
{
value: "gelb",
synonyms: ["kanariengelb", "flachs", "mais", "honig", "bernstein", "blond"],
},
],
};
@@ -1,3 +1,3 @@
module.exports = {
testIntent: ["This is a test", "A second test utterances"],
};
"this should be shadowed by utterances.ts": true
}
@@ -1,3 +1,3 @@
{
"this should be shadowed by utterances.ts": true
}
"this should be shadowed by utterances.js": true
}
@@ -0,0 +1,3 @@
module.exports = {
testIntent: ["This is a test", "A second test utterances"],
};
@@ -27,4 +27,12 @@ export class LocalesLoaderMock implements LocalesLoader {
},
};
}

public getTranslations() {
return {};
}

public getLocales() {
return undefined;
}
}
@@ -0,0 +1,11 @@
import * as i18next from "i18next";
import { Container } from "inversify-components";
import { UnifierConfiguration } from "../../../src/assistant-source";

export function configureUnifier(container: Container, utterancePath: string) {
const config: UnifierConfiguration = {
utterancePath,
};

container.componentRegistry.lookup("core:unifier").addConfiguration(config);
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.