mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-13 07:10:44 -05:00
Fix issues around locales (!332)
- Remove locale check for give command (Now handled in the localeService itself) - Fix variable names/spelling/lowercase in LocaleService.getPlatformForServerLocale (should be no change in behaviour) - Rewrite LocaleService.getPlatformForClientLocale to not depend on serverSupportedLocales, and instead just use the tables for validity checking - Expand fallback of client locale handling to include the region code (Handles Czech and Korean locales) - Write tests for locales Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Reviewed-on: SPT-AKI/Server#332 Co-authored-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com> Co-committed-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com>
This commit is contained in:
parent
5254e9753e
commit
adab18e3fb
@ -129,15 +129,6 @@ export class GiveSptCommand implements ISptCommand
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
locale = result[4] ? result[4] : this.localeService.getDesiredGameLocale() ?? "en";
|
locale = result[4] ? result[4] : this.localeService.getDesiredGameLocale() ?? "en";
|
||||||
if (!this.localeService.getServerSupportedLocales().includes(locale))
|
|
||||||
{
|
|
||||||
this.mailSendService.sendUserMessageToPlayer(
|
|
||||||
sessionId,
|
|
||||||
commandHandler,
|
|
||||||
`Unknown locale "${locale}". Use \"help\" for more information.`,
|
|
||||||
);
|
|
||||||
return request.dialogId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { inject, injectable } from "tsyringe";
|
import { inject, injectable } from "tsyringe";
|
||||||
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
||||||
import { ILocaleConfig } from "@spt-aki/models/spt/config/ILocaleConfig";
|
import { ILocaleConfig } from "@spt-aki/models/spt/config/ILocaleConfig";
|
||||||
|
import { ILocaleBase } from "@spt-aki/models/spt/server/ILocaleBase";
|
||||||
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
||||||
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
||||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
||||||
@ -12,6 +13,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|||||||
export class LocaleService
|
export class LocaleService
|
||||||
{
|
{
|
||||||
protected localeConfig: ILocaleConfig;
|
protected localeConfig: ILocaleConfig;
|
||||||
|
protected localesTable: ILocaleBase;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject("WinstonLogger") protected logger: ILogger,
|
@inject("WinstonLogger") protected logger: ILogger,
|
||||||
@ -20,6 +22,7 @@ export class LocaleService
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.localeConfig = this.configServer.getConfig(ConfigTypes.LOCALE);
|
this.localeConfig = this.configServer.getConfig(ConfigTypes.LOCALE);
|
||||||
|
this.localesTable = this.databaseServer.getTables().locales;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,7 +31,7 @@ export class LocaleService
|
|||||||
*/
|
*/
|
||||||
public getLocaleDb(): Record<string, string>
|
public getLocaleDb(): Record<string, string>
|
||||||
{
|
{
|
||||||
const desiredLocale = this.databaseServer.getTables().locales.global[this.getDesiredGameLocale()];
|
const desiredLocale = this.localesTable.global[this.getDesiredGameLocale()];
|
||||||
if (desiredLocale)
|
if (desiredLocale)
|
||||||
{
|
{
|
||||||
return desiredLocale;
|
return desiredLocale;
|
||||||
@ -38,7 +41,7 @@ export class LocaleService
|
|||||||
`Unable to find desired locale file using locale: ${this.getDesiredGameLocale()} from config/locale.json, falling back to 'en'`,
|
`Unable to find desired locale file using locale: ${this.getDesiredGameLocale()} from config/locale.json, falling back to 'en'`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.databaseServer.getTables().locales.global.en;
|
return this.localesTable.global.en;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,29 +98,30 @@ export class LocaleService
|
|||||||
*/
|
*/
|
||||||
protected getPlatformForServerLocale(): string
|
protected getPlatformForServerLocale(): string
|
||||||
{
|
{
|
||||||
const platformLocale = new Intl.Locale(Intl.DateTimeFormat().resolvedOptions().locale);
|
const platformLocale = this.getPlatformLocale();
|
||||||
if (!platformLocale)
|
if (!platformLocale)
|
||||||
{
|
{
|
||||||
this.logger.warning("System langauge could not be found, falling back to english");
|
this.logger.warning("System language could not be found, falling back to english");
|
||||||
|
|
||||||
return "en";
|
return "en";
|
||||||
}
|
}
|
||||||
|
|
||||||
const localeCode = platformLocale.baseName.toLowerCase();
|
const baseNameCode = platformLocale.baseName.toLowerCase();
|
||||||
if (!this.localeConfig.serverSupportedLocales.includes(localeCode))
|
if (!this.localeConfig.serverSupportedLocales.includes(baseNameCode))
|
||||||
{
|
{
|
||||||
// Chek if base language (e.g. CN / EN / DE) exists
|
// Chek if base language (e.g. CN / EN / DE) exists
|
||||||
if (this.localeConfig.serverSupportedLocales.includes(platformLocale.language))
|
const languageCode = platformLocale.language.toLocaleLowerCase();
|
||||||
|
if (this.localeConfig.serverSupportedLocales.includes(languageCode))
|
||||||
{
|
{
|
||||||
return platformLocale.language;
|
return languageCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.warning(`Unsupported system langauge found: ${localeCode}, falling back to english`);
|
this.logger.warning(`Unsupported system language found: ${baseNameCode}, falling back to english`);
|
||||||
|
|
||||||
return "en";
|
return "en";
|
||||||
}
|
}
|
||||||
|
|
||||||
return localeCode;
|
return baseNameCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,26 +130,29 @@ export class LocaleService
|
|||||||
*/
|
*/
|
||||||
protected getPlatformForClientLocale(): string
|
protected getPlatformForClientLocale(): string
|
||||||
{
|
{
|
||||||
const platformLocale = new Intl.Locale(Intl.DateTimeFormat().resolvedOptions().locale);
|
const platformLocale = this.getPlatformLocale();
|
||||||
if (!platformLocale)
|
if (!platformLocale)
|
||||||
{
|
{
|
||||||
this.logger.warning("System langauge could not be found, falling back to english");
|
this.logger.warning("System language could not be found, falling back to english");
|
||||||
|
|
||||||
return "en";
|
return "en";
|
||||||
}
|
}
|
||||||
|
|
||||||
const langaugeCode = platformLocale.language.toLowerCase();
|
const baseNameCode = platformLocale.baseName?.toLocaleLowerCase();
|
||||||
if (!this.localeConfig.serverSupportedLocales.includes(langaugeCode))
|
if (baseNameCode && this.localesTable.global[baseNameCode])
|
||||||
{
|
{
|
||||||
this.logger.warning(`Unsupported system langauge found: ${langaugeCode}, falling back to english`);
|
return baseNameCode;
|
||||||
|
|
||||||
return "en";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BSG map Czech to CZ for some reason
|
const languageCode = platformLocale.language?.toLowerCase();
|
||||||
if (platformLocale.language === "cs")
|
if (languageCode && this.localesTable.global[languageCode])
|
||||||
{
|
{
|
||||||
return "cz";
|
return languageCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const regionCode = platformLocale.region?.toLocaleLowerCase();
|
||||||
|
if (regionCode && this.localesTable.global[regionCode])
|
||||||
|
{
|
||||||
|
return regionCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// BSG map DE to GE some reason
|
// BSG map DE to GE some reason
|
||||||
@ -154,6 +161,16 @@ export class LocaleService
|
|||||||
return "ge";
|
return "ge";
|
||||||
}
|
}
|
||||||
|
|
||||||
return langaugeCode;
|
this.logger.warning(`Unsupported system language found: ${languageCode}, falling back to english`);
|
||||||
|
return "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is in a function so we can overwrite it during testing
|
||||||
|
* @returns The current platform locale
|
||||||
|
*/
|
||||||
|
protected getPlatformLocale(): Intl.Locale
|
||||||
|
{
|
||||||
|
return new Intl.Locale(Intl.DateTimeFormat().resolvedOptions().locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
124
project/tests/services/LocaleService.test.ts
Normal file
124
project/tests/services/LocaleService.test.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { container } from "tsyringe";
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { LocaleService } from "@spt-aki/services/LocaleService";
|
||||||
|
|
||||||
|
describe("LocaleService", () =>
|
||||||
|
{
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
|
let localeService: any; // Using "any" to access private/protected methods without type errors.
|
||||||
|
|
||||||
|
beforeEach(() =>
|
||||||
|
{
|
||||||
|
localeService = container.resolve<LocaleService>("LocaleService");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() =>
|
||||||
|
{
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("ValidateServerLocale", () =>
|
||||||
|
{
|
||||||
|
it("should return 'en' for 'en-US'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("en-US");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForServerLocale()).toBe("en");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return 'ko' for 'ko-KR'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("ko-KR");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForServerLocale()).toBe("ko");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return 'pt-pt' for 'pt-PT'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("pt-PT");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForServerLocale()).toBe("pt-pt");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return 'pt-br' for 'pt-BR'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("pt-BR");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForServerLocale()).toBe("pt-br");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("ValidateClientLocale", () =>
|
||||||
|
{
|
||||||
|
it("should return 'en' for 'en-US'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("en-US");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForClientLocale()).toBe("en");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return 'kr' for 'ko-KR'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("ko-KR");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForClientLocale()).toBe("kr");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return 'es-mx' for 'es-MX'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("es-MX");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForClientLocale()).toBe("es-mx");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return 'cz' for 'cs-CZ'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("cs-CZ");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForClientLocale()).toBe("cz");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return 'ge' for 'de-DE'", () =>
|
||||||
|
{
|
||||||
|
// Override the get locale so we control what the input is
|
||||||
|
localeService.getPlatformLocale = () =>
|
||||||
|
{
|
||||||
|
return new Intl.Locale("de-DE");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(localeService.getPlatformForClientLocale()).toBe("ge");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user