mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
Case-insensitive lang search for i8n, along with support for match just lang, without region, ie. 'en' == 'en-US'
This commit is contained in:
@@ -48,7 +48,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -Rf build",
|
"clean": "rm -Rf build",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"dev": "BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true BONOB_SONOS_AUTO_REGISTER=false nodemon ./src/app.ts",
|
"dev": "BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true nodemon ./src/app.ts",
|
||||||
"register-dev": "ts-node ./src/register.ts http://$(hostname):4534",
|
"register-dev": "ts-node ./src/register.ts http://$(hostname):4534",
|
||||||
"test": "jest --testPathIgnorePatterns=build"
|
"test": "jest --testPathIgnorePatterns=build"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,13 @@ const translations: Record<LANG, Record<KEY, string>> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const translationsLookup = Object.keys(translations).reduce((lookups, lang) => {
|
||||||
|
lookups.set(lang, translations[lang as LANG]);
|
||||||
|
lookups.set(lang.toLocaleLowerCase(), translations[lang as LANG]);
|
||||||
|
lookups.set(lang.toLocaleLowerCase().split("-")[0]!, translations[lang as LANG]);
|
||||||
|
return lookups;
|
||||||
|
}, new Map<string, Record<KEY, string>>())
|
||||||
|
|
||||||
export const randomLang = () => _.shuffle(["en-US", "nl-NL"])[0]!;
|
export const randomLang = () => _.shuffle(["en-US", "nl-NL"])[0]!;
|
||||||
|
|
||||||
export const asLANGs = (acceptLanguageHeader: string | undefined) =>
|
export const asLANGs = (acceptLanguageHeader: string | undefined) =>
|
||||||
@@ -135,7 +142,7 @@ export const keys = (lang: LANG = "en-US") => Object.keys(translations[lang]);
|
|||||||
export default (serviceName: string): I8N =>
|
export default (serviceName: string): I8N =>
|
||||||
(...langs: string[]): Lang => {
|
(...langs: string[]): Lang => {
|
||||||
const langToUse =
|
const langToUse =
|
||||||
langs.map((l) => translations[l as LANG]).find((it) => it) ||
|
langs.map((l) => translationsLookup.get(l as LANG)).find((it) => it) ||
|
||||||
translations["en-US"];
|
translations["en-US"];
|
||||||
return (key: KEY) => {
|
return (key: KEY) => {
|
||||||
const value = langToUse[key]?.replace(
|
const value = langToUse[key]?.replace(
|
||||||
|
|||||||
@@ -87,7 +87,10 @@ function server(
|
|||||||
app.set("view engine", "eta");
|
app.set("view engine", "eta");
|
||||||
app.set("views", "./web/views");
|
app.set("views", "./web/views");
|
||||||
|
|
||||||
const langFor = (req: Request) => i8n(...asLANGs(req.headers["accept-language"]))
|
const langFor = (req: Request) => {
|
||||||
|
logger.debug(`${req.path} (req[accept-language]=${req.headers["accept-language"]})`);
|
||||||
|
return i8n(...asLANGs(req.headers["accept-language"]));
|
||||||
|
}
|
||||||
|
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
const lang = langFor(req);
|
const lang = langFor(req);
|
||||||
|
|||||||
@@ -536,9 +536,10 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
.then(splitId(id))
|
.then(splitId(id))
|
||||||
.then(({ musicLibrary, accessToken, type, typeId }) => {
|
.then(({ musicLibrary, accessToken, type, typeId }) => {
|
||||||
const paging = { _index: index, _count: count };
|
const paging = { _index: index, _count: count };
|
||||||
const lang = i8n((headers["accept-language"] || "en-US") as LANG);
|
const acceptLanguage = headers["accept-language"];
|
||||||
|
const lang = i8n((acceptLanguage || "en-US") as LANG);
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Fetching metadata type=${type}, typeId=${typeId}, lang=${lang}`
|
`Fetching metadata type=${type}, typeId=${typeId}, acceptLanguage=${acceptLanguage}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const albums = (q: AlbumQuery): Promise<GetMetadataResponse> =>
|
const albums = (q: AlbumQuery): Promise<GetMetadataResponse> =>
|
||||||
|
|||||||
@@ -54,79 +54,129 @@ describe("i8n", () => {
|
|||||||
|
|
||||||
describe("fetching translations", () => {
|
describe("fetching translations", () => {
|
||||||
describe("with a single lang", () => {
|
describe("with a single lang", () => {
|
||||||
describe("and there is no templating", () => {
|
describe("and the lang is not represented", () => {
|
||||||
it("should return the value", () => {
|
describe("and there is no templating", () => {
|
||||||
expect(i8n("foo")("en-US")("artists")).toEqual("Artists");
|
it("should return the en-US value", () => {
|
||||||
expect(i8n("foo")("nl-NL")("artists")).toEqual("Artiesten");
|
expect(i8n("foo")("en-AU" as LANG)("artists")).toEqual("Artists");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and there is templating of the service name", () => {
|
||||||
|
it("should return the en-US value templated", () => {
|
||||||
|
expect(i8n("service123")("en-AU" as LANG)("AppLinkMessage")).toEqual(
|
||||||
|
"Linking sonos with service123"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("and there is templating of the service name", () => {
|
describe("and the lang is represented", () => {
|
||||||
it("should return the value", () => {
|
describe("and there is no templating", () => {
|
||||||
expect(i8n("service123")("en-US")("AppLinkMessage")).toEqual(
|
it("should return the value", () => {
|
||||||
"Linking sonos with service123"
|
expect(i8n("foo")("en-US")("artists")).toEqual("Artists");
|
||||||
);
|
expect(i8n("foo")("nl-NL")("artists")).toEqual("Artiesten");
|
||||||
expect(i8n("service456")("nl-NL")("AppLinkMessage")).toEqual(
|
});
|
||||||
"Sonos koppelen aan service456"
|
});
|
||||||
);
|
|
||||||
|
describe("and there is templating of the service name", () => {
|
||||||
|
it("should return the value", () => {
|
||||||
|
expect(i8n("service123")("en-US")("AppLinkMessage")).toEqual(
|
||||||
|
"Linking sonos with service123"
|
||||||
|
);
|
||||||
|
expect(i8n("service456")("nl-NL")("AppLinkMessage")).toEqual(
|
||||||
|
"Sonos koppelen aan service456"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("with multiple langs", () => {
|
describe("with multiple langs", () => {
|
||||||
describe("and the first lang is a match", () => {
|
function itShouldReturn(serviceName: string, langs: string[], key: KEY, expected: string) {
|
||||||
|
it(`should return '${expected}' for the serviceName=${serviceName}, langs=${langs}`, () => {
|
||||||
|
expect(i8n(serviceName)(...langs)(key)).toEqual(expected);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("and the first lang is an exact match", () => {
|
||||||
describe("and there is no templating", () => {
|
describe("and there is no templating", () => {
|
||||||
it("should return the value for the first lang", () => {
|
itShouldReturn("foo", ["en-US", "nl-NL"], "artists", "Artists");
|
||||||
expect(i8n("foo")("en-US", "nl-NL")("artists")).toEqual("Artists");
|
itShouldReturn("foo", ["nl-NL", "en-US"], "artists", "Artiesten");
|
||||||
expect(i8n("foo")("nl-NL", "en-US")("artists")).toEqual("Artiesten");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("and there is templating of the service name", () => {
|
describe("and there is templating of the service name", () => {
|
||||||
it("should return the value for the firt lang", () => {
|
itShouldReturn("service123", ["en-US", "nl-NL"], "AppLinkMessage", "Linking sonos with service123");
|
||||||
expect(i8n("service123")("en-US", "nl-NL")("AppLinkMessage")).toEqual(
|
itShouldReturn("service456", ["nl-NL", "en-US"], "AppLinkMessage", "Sonos koppelen aan service456");
|
||||||
"Linking sonos with service123"
|
|
||||||
);
|
|
||||||
expect(i8n("service456")("nl-NL", "en-US")("AppLinkMessage")).toEqual(
|
|
||||||
"Sonos koppelen aan service456"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("and the first lang is not a match, however there is a match in the provided langs", () => {
|
describe("and the first lang is a case insensitive match", () => {
|
||||||
describe("and there is no templating", () => {
|
describe("and there is no templating", () => {
|
||||||
it("should return the value for the first lang", () => {
|
itShouldReturn("foo", ["en-us", "nl-NL"], "artists", "Artists");
|
||||||
expect(i8n("foo")("something", "en-US", "nl-NL")("artists")).toEqual("Artists");
|
itShouldReturn("foo", ["nl-nl", "en-US"], "artists", "Artiesten");
|
||||||
expect(i8n("foo")("something", "nl-NL", "en-US")("artists")).toEqual("Artiesten");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("and there is templating of the service name", () => {
|
describe("and there is templating of the service name", () => {
|
||||||
it("should return the value for the firt lang", () => {
|
itShouldReturn("service123", ["en-us", "nl-NL"], "AppLinkMessage", "Linking sonos with service123");
|
||||||
expect(i8n("service123")("something", "en-US", "nl-NL")("AppLinkMessage")).toEqual(
|
itShouldReturn("service456", ["nl-nl", "en-US"], "AppLinkMessage", "Sonos koppelen aan service456");
|
||||||
"Linking sonos with service123"
|
});
|
||||||
);
|
});
|
||||||
expect(i8n("service456")("something", "nl-NL", "en-US")("AppLinkMessage")).toEqual(
|
|
||||||
"Sonos koppelen aan service456"
|
describe("and the first lang is a lang match without region", () => {
|
||||||
);
|
describe("and there is no templating", () => {
|
||||||
});
|
itShouldReturn("foo", ["en", "nl-NL"], "artists", "Artists");
|
||||||
|
itShouldReturn("foo", ["nl", "en-US"], "artists", "Artiesten");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and there is templating of the service name", () => {
|
||||||
|
itShouldReturn("service123", ["en", "nl-NL"], "AppLinkMessage", "Linking sonos with service123");
|
||||||
|
itShouldReturn("service456", ["nl", "en-US"], "AppLinkMessage", "Sonos koppelen aan service456");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the first lang is not a match, however there is an exact match in the provided langs", () => {
|
||||||
|
describe("and there is no templating", () => {
|
||||||
|
itShouldReturn("foo", ["something", "en-US", "nl-NL"], "artists", "Artists")
|
||||||
|
itShouldReturn("foo", ["something", "nl-NL", "en-US"], "artists", "Artiesten")
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and there is templating of the service name", () => {
|
||||||
|
itShouldReturn("service123", ["something", "en-US", "nl-NL"], "AppLinkMessage", "Linking sonos with service123")
|
||||||
|
itShouldReturn("service456", ["something", "nl-NL", "en-US"], "AppLinkMessage", "Sonos koppelen aan service456")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the first lang is not a match, however there is a case insensitive match in the provided langs", () => {
|
||||||
|
describe("and there is no templating", () => {
|
||||||
|
itShouldReturn("foo", ["something", "en-us", "nl-nl"], "artists", "Artists")
|
||||||
|
itShouldReturn("foo", ["something", "nl-nl", "en-us"], "artists", "Artiesten")
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and there is templating of the service name", () => {
|
||||||
|
itShouldReturn("service123", ["something", "en-us", "nl-nl"], "AppLinkMessage", "Linking sonos with service123")
|
||||||
|
itShouldReturn("service456", ["something", "nl-nl", "en-us"], "AppLinkMessage", "Sonos koppelen aan service456")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the first lang is not a match, however there is a lang match without region", () => {
|
||||||
|
describe("and there is no templating", () => {
|
||||||
|
itShouldReturn("foo", ["something", "en", "nl-nl"], "artists", "Artists")
|
||||||
|
itShouldReturn("foo", ["something", "nl", "en-us"], "artists", "Artiesten")
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and there is templating of the service name", () => {
|
||||||
|
itShouldReturn("service123", ["something", "en", "nl-nl"], "AppLinkMessage", "Linking sonos with service123")
|
||||||
|
itShouldReturn("service456", ["something", "nl", "en-us"], "AppLinkMessage", "Sonos koppelen aan service456")
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("and no lang is a match", () => {
|
describe("and no lang is a match", () => {
|
||||||
describe("and there is no templating", () => {
|
describe("and there is no templating", () => {
|
||||||
it("should return the value for the first lang", () => {
|
itShouldReturn("foo", ["something", "something2"], "artists", "Artists")
|
||||||
expect(i8n("foo")("something", "something2")("artists")).toEqual("Artists");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("and there is templating of the service name", () => {
|
describe("and there is templating of the service name", () => {
|
||||||
it("should return the value for the firt lang", () => {
|
itShouldReturn("service123", ["something", "something2"], "AppLinkMessage", "Linking sonos with service123")
|
||||||
expect(i8n("service123")("something", "something2")("AppLinkMessage")).toEqual(
|
|
||||||
"Linking sonos with service123"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -139,20 +189,5 @@ describe("i8n", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when the lang is not represented", () => {
|
|
||||||
describe("and there is no templating", () => {
|
|
||||||
it("should return the en-US value", () => {
|
|
||||||
expect(i8n("foo")("en-AU" as LANG)("artists")).toEqual("Artists");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("and there is templating of the service name", () => {
|
|
||||||
it("should return the en-US value templated", () => {
|
|
||||||
expect(i8n("service123")("en-AU" as LANG)("AppLinkMessage")).toEqual(
|
|
||||||
"Linking sonos with service123"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user