@metamask/snaps-utils
Version:
A collection of utilities for MetaMask Snaps
128 lines • 5.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateSnapManifestLocalizations = exports.getLocalizedSnapManifest = exports.translate = exports.TRANSLATION_REGEX = exports.getLocalizationFile = exports.getValidatedLocalizationFiles = exports.LocalizationFileStruct = exports.LOCALIZABLE_FIELDS = void 0;
const snaps_sdk_1 = require("@metamask/snaps-sdk");
const superstruct_1 = require("@metamask/superstruct");
const json_1 = require("./json.cjs");
exports.LOCALIZABLE_FIELDS = ['description', 'proposedName'];
exports.LocalizationFileStruct = (0, superstruct_1.object)({
locale: (0, superstruct_1.string)(),
messages: (0, superstruct_1.record)((0, superstruct_1.string)(), (0, superstruct_1.object)({
message: (0, superstruct_1.string)(),
description: (0, superstruct_1.optional)((0, superstruct_1.string)()),
})),
});
/**
* Validate a list of localization files.
*
* @param localizationFiles - The localization files to validate.
* @returns The validated localization files.
* @throws If any of the files are considered invalid.
*/
function getValidatedLocalizationFiles(localizationFiles) {
for (const file of localizationFiles) {
try {
file.result = (0, superstruct_1.create)((0, json_1.parseJson)(file.toString()), exports.LocalizationFileStruct);
}
catch (error) {
if (error instanceof superstruct_1.StructError) {
throw new Error(`Failed to validate localization file "${file.path}": ${error.message}.`);
}
if (error instanceof SyntaxError) {
throw new Error(`Failed to parse localization file "${file.path}" as JSON.`);
}
throw error;
}
}
return localizationFiles;
}
exports.getValidatedLocalizationFiles = getValidatedLocalizationFiles;
/**
* Get the localization file for a given locale. If the locale is not found,
* the English localization file will be returned.
*
* @param locale - The locale to use.
* @param localizationFiles - The localization files to use.
* @returns The localization file, or `undefined` if no localization file was
* found.
*/
function getLocalizationFile(locale, localizationFiles) {
const file = localizationFiles.find((localizationFile) => localizationFile.locale === locale);
if (!file) {
return localizationFiles.find((localizationFile) => localizationFile.locale === 'en');
}
return file;
}
exports.getLocalizationFile = getLocalizationFile;
exports.TRANSLATION_REGEX = /\{\{\s?([a-zA-Z0-9-_\s]+)\s?\}\}/gu;
/**
* Translate a string using a localization file. This will replace all instances
* of `{{key}}` with the localized version of `key`.
*
* @param value - The string to translate.
* @param file - The localization file to use, or `undefined` if no localization
* file was found.
* @returns The translated string.
* @throws If the string contains a key that is not present in the localization
* file, or if no localization file was found.
*/
function translate(value, file) {
const matches = value.matchAll(exports.TRANSLATION_REGEX);
const array = Array.from(matches);
return array.reduce((result, [match, key]) => {
if (!file) {
throw new Error(`Failed to translate "${value}": No localization file found.`);
}
const translation = file.messages[key.trim()];
if (!translation) {
throw new Error(`Failed to translate "${value}": No translation found for "${key.trim()}" in "${file.locale}" file.`);
}
return result.replace(match, translation.message);
}, value);
}
exports.translate = translate;
/**
* Get the localized Snap manifest for a given locale. This will replace all
* localized strings in the manifest with the localized version.
*
* @param snapManifest - The Snap manifest to localize.
* @param locale - The locale to use.
* @param localizationFiles - The localization files to use.
* @returns The localized Snap manifest.
*/
function getLocalizedSnapManifest(snapManifest, locale, localizationFiles) {
const file = getLocalizationFile(locale, localizationFiles);
return exports.LOCALIZABLE_FIELDS.reduce((manifest, field) => {
const translation = translate(manifest[field], file);
return {
...manifest,
[field]: translation,
};
}, snapManifest);
}
exports.getLocalizedSnapManifest = getLocalizedSnapManifest;
/**
* Validate the localization files for a Snap manifest.
*
* @param snapManifest - The Snap manifest to validate.
* @param localizationFiles - The localization files to validate.
* @throws If the manifest cannot be localized.
*/
function validateSnapManifestLocalizations(snapManifest, localizationFiles) {
try {
// `translate` throws if the manifest cannot be localized, so we just attempt
// to translate the manifest using all localization files.
localizationFiles
.filter((file) => file.locale !== 'en')
.forEach((file) => {
getLocalizedSnapManifest(snapManifest, file.locale, localizationFiles);
});
// The manifest must be localizable in English.
getLocalizedSnapManifest(snapManifest, 'en', localizationFiles);
}
catch (error) {
throw new Error(`Failed to localize Snap manifest: ${(0, snaps_sdk_1.getErrorMessage)(error)}`);
}
}
exports.validateSnapManifestLocalizations = validateSnapManifestLocalizations;
//# sourceMappingURL=localization.cjs.map