UNPKG

@aleksejdix/ally-bcp47

Version:

TypeScript package for working with BCP-47 language tags

132 lines 4.4 kB
import { ValidationErrorType, } from "../types/index.js"; import { LANGUAGE_TAG_REGEX } from "../utils/constants.js"; import { parseLanguageTag } from "./parser.js"; import { validateTagAgainstRegistry, normalizeTag } from "../registry/index.js"; /** * Default validation options */ const DEFAULT_VALIDATION_OPTIONS = { checkRegistry: true, warnOnDeprecated: true, warnOnRedundantScript: true, }; /** * Validates a language tag against the BCP-47 specification * * @param tag The language tag to validate * @param options Validation options * @returns Validation result */ export function validateLanguageTag(tag, options = {}) { // Merge options with defaults const opts = { ...DEFAULT_VALIDATION_OPTIONS, ...options }; // Initial quick check with regex const isQuickMatch = LANGUAGE_TAG_REGEX.test(tag); // Quick fail for obviously malformed tags if (!isQuickMatch) { return { isWellFormed: false, isValid: false, errors: [ { type: ValidationErrorType.MALFORMED_TAG, message: `Language tag "${tag}" does not match the BCP-47 syntax`, }, ], }; } // Parse the tag const { parsed, errors } = parseLanguageTag(tag); // If parsing failed, return the errors if (!parsed) { return { isWellFormed: false, isValid: false, errors, }; } // Collect errors and warnings const syntaxErrors = [...errors]; const registryErrors = []; const warnings = []; // If registry checking is enabled, validate against the registry if (opts.checkRegistry && parsed) { const registryValidation = validateTagAgainstRegistry(parsed); if (!registryValidation.valid) { for (const problem of registryValidation.problems) { registryErrors.push({ type: ValidationErrorType.UNKNOWN_SUBTAG, message: problem.message, subtag: problem.subtag, subtagType: problem.subtagType, suggestedReplacement: problem.suggestedReplacement, }); } } } // Normalize the tag to canonical form if (parsed && parsed.tag) { // Store the original parsed tag const originalTag = parsed.tag; // Normalize to canonical form const normalizedTag = normalizeTag(originalTag); // Update the tag in the parsed result parsed.tag = normalizedTag; } // Determine if the tag is well-formed and valid const isWellFormed = syntaxErrors.length === 0; const isValid = isWellFormed && registryErrors.length === 0; // Combine all errors const allErrors = [...syntaxErrors, ...registryErrors]; return { isWellFormed, isValid, tag: parsed, errors: allErrors.length > 0 ? allErrors : undefined, warnings: warnings.length > 0 ? warnings : undefined, }; } /** * Checks if a language tag is well-formed according to BCP-47 syntax rules * * @param tag The language tag to check * @returns True if the tag is well-formed, false otherwise */ export function isWellFormed(tag) { const result = validateLanguageTag(tag, { checkRegistry: false }); return result.isWellFormed; } /** * Checks if a language tag is valid (well-formed and all subtags exist in registry) * * @param tag The language tag to check * @returns True if the tag is valid, false otherwise */ export function isValid(tag) { const result = validateLanguageTag(tag, { checkRegistry: true }); return result.isValid; } /** * Parses a language tag string into its component parts without validation * * @param tag The language tag to parse * @returns The parsed language tag or null if parsing fails */ export function parseTag(tag) { const { parsed } = parseLanguageTag(tag); return parsed; } /** * Canonicalizes a language tag to its canonical form * * @param tag The language tag to canonicalize * @returns The canonicalized tag or null if the tag is invalid */ export function canonicalizeTag(tag) { const result = validateLanguageTag(tag); if (!result.isWellFormed || !result.tag) { return null; } return result.tag.tag; } //# sourceMappingURL=validator.js.map