diff --git a/BuildTools/Scripts/convert-a11y-display-guide-localizations.js b/BuildTools/Scripts/convert-a11y-display-guide-localizations.js deleted file mode 100644 index 9628a342d..000000000 --- a/BuildTools/Scripts/convert-a11y-display-guide-localizations.js +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright 2025 Readium Foundation. All rights reserved. - * Use of this source code is governed by the BSD-style license - * available in the top-level LICENSE file of the project. - * - * This script can be used to convert the localized files from https://github.com/w3c/publ-a11y-display-guide-localizations - * into other output formats for various platforms. - */ - -const fs = require('fs'); -const path = require('path'); -const [inputFolder, outputFormat, outputFolder, keyPrefix = ''] = process.argv.slice(2); - -/** - * Ends the script with the given error message. - */ -function fail(message) { - console.error(`Error: ${message}`); - process.exit(1); -} - -/** - * Converter for Apple localized strings. - */ -function convertApple(lang, version, keys, keyPrefix, write) { - let disclaimer = `DO NOT EDIT. File generated automatically from v${version} of the ${lang} JSON strings.`; - - let stringsOutput = `// ${disclaimer}\n\n`; - for (const [key, value] of Object.entries(keys)) { - stringsOutput += `"${keyPrefix}${key}" = "${value}";\n`; - } - let stringsFile = path.join(`Resources/${lang}.lproj`, 'W3CAccessibilityMetadataDisplayGuide.strings'); - write(stringsFile, stringsOutput); - - // Using the "base" language, we will generate a static list of string keys to validate them at compile time. - if (lang == 'en-US') { - writeSwiftExtensions(disclaimer, keys, keyPrefix, write); - } -} - -/** - * Generates a static list of string keys to validate them at compile time. - */ -function writeSwiftExtensions(disclaimer, keys, keyPrefix, write) { - let keysOutput = `// -// Copyright 2025 Readium Foundation. All rights reserved. -// Use of this source code is governed by the BSD-style license -// available in the top-level LICENSE file of the project. -// - -// ${disclaimer}\n\npublic extension AccessibilityDisplayString {\n` - let keysList = Object.keys(keys) - .filter((k) => !k.endsWith("-descriptive")) - .map((k) => removeSuffix(k, "-compact")); - for (const key of keysList) { - keysOutput += ` static let ${convertKebabToCamelCase(key)}: Self = "${keyPrefix}${key}"\n`; - } - keysOutput += "}\n" - write("Publication/Accessibility/AccessibilityDisplayString+Generated.swift", keysOutput); -} - -const converters = { - apple: convertApple -}; - -if (!inputFolder || !outputFormat || !outputFolder) { - console.error('Usage: node convert.js [key-prefix]'); - process.exit(1); -} - -const langFolder = path.join(inputFolder, 'lang'); -if (!fs.existsSync(langFolder)) { - fail(`the specified input folder does not contain a 'lang' directory`); -} - -const convert = converters[outputFormat]; -if (!convert) { - fail(`unrecognized output format: ${outputFormat}, try: ${Object.keys(converters).join(', ')}.`); -} - -fs.readdir(langFolder, (err, langDirs) => { - if (err) { - fail(`reading directory: ${err.message}`); - } - - langDirs.forEach(langDir => { - const langDirPath = path.join(langFolder, langDir); - - fs.readdir(langDirPath, (err, files) => { - if (err) { - fail(`reading language directory ${langDir}: ${err.message}`); - } - - files.forEach(file => { - const filePath = path.join(langDirPath, file); - if (path.extname(file) === '.json') { - fs.readFile(filePath, 'utf8', (err, data) => { - if (err) { - console.error(`Error reading file ${file}: ${err.message}`); - return; - } - - try { - const jsonData = JSON.parse(data); - const version = jsonData["metadata"]["version"]; - convert(langDir, version, parseJsonKeys(jsonData), keyPrefix, write); - } catch (err) { - fail(`parsing JSON from file ${file}: ${err.message}`); - } - }); - } - }); - }); - }); -}); - -/** - * Writes the given content to the file path relative to the outputFolder provided in the CLI arguments. - */ -function write(relativePath, content) { - const outputPath = path.join(outputFolder, relativePath); - const outputDir = path.dirname(outputPath); - - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); - } - - fs.writeFile(outputPath, content, 'utf8', err => { - if (err) { - fail(`writing file ${outputPath}: ${err.message}`); - } else { - console.log(`Wrote ${outputPath}`); - } - }); -} - -/** - * Collects the JSON translation keys. - */ -function parseJsonKeys(obj) { - const keys = {}; - for (const key in obj) { - if (key === 'metadata') continue; // Ignore the metadata key - if (typeof obj[key] === 'object') { - for (const subKey in obj[key]) { - if (typeof obj[key][subKey] === 'object') { - for (const innerKey in obj[key][subKey]) { - const fullKey = `${subKey}-${innerKey}`; - keys[fullKey] = obj[key][subKey][innerKey]; - } - } else { - keys[subKey] = obj[key][subKey]; - } - } - } - } - return keys; -} - -function convertKebabToCamelCase(string) { - return string - .split('-') - .map((word, index) => { - if (index === 0) { - return word; - } - return word.charAt(0).toUpperCase() + word.slice(1); - }) - .join(''); -} - -function removeSuffix(str, suffix) { - if (str.endsWith(suffix)) { - return str.slice(0, -suffix.length); - } - return str; -} \ No newline at end of file diff --git a/BuildTools/Scripts/convert-thorium-localizations.js b/BuildTools/Scripts/convert-thorium-localizations.js index c2cd4dcfd..3e05ec624 100644 --- a/BuildTools/Scripts/convert-thorium-localizations.js +++ b/BuildTools/Scripts/convert-thorium-localizations.js @@ -10,167 +10,282 @@ const fs = require('fs'); const fsPromises = fs.promises; const path = require('path'); +const { generateAccessibilityDisplayStringExtensions } = require('./generate-a11y-extensions'); + +/** + * Plural suffixes used in thorium-locales JSON files (underscore format). + */ +const PLURAL_SUFFIXES = ['_zero', '_one', '_two', '_few', '_many', '_other']; + +/** + * Languages to process from thorium-locales. + */ +const LANGUAGES = ['en', 'fr', 'it']; + +/** + * Represents a localization key with support for plural forms. + */ +class LocalizationKey { + constructor(base, pluralForm = null) { + this.base = base; + this.pluralForm = pluralForm; + } + + stripPrefix(prefix) { + if (!prefix || !this.base.startsWith(prefix)) { + return this; + } + return new LocalizationKey(this.base.slice(prefix.length), this.pluralForm); + } + + toCamelCase() { + const camel = this.base + .split('-') + .map((word, index) => (index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1))) + .join(''); + return new LocalizationKey(camel, this.pluralForm); + } + + matchesAnyPrefix(prefixes) { + return prefixes.some(prefix => this.base.startsWith(prefix)); + } + + toString() { + return this.base; + } +} + +/** + * Represents a localization entry (key-value pair). + */ +class LocalizationEntry { + constructor(key, value, sourceKey = null) { + this.key = key; + this.value = value; + this.sourceKey = sourceKey; + this._placeholders = null; + } + + get placeholders() { + if (this._placeholders === null) { + const regex = /\{\{\s*(\w+)\s*\}\}/g; + const found = []; + let match; + while ((match = regex.exec(this.value)) !== null) { + if (!found.includes(match[1])) { + found.push(match[1]); + } + } + this._placeholders = found; + } + return this._placeholders; + } + + get hasPlaceholders() { + return this.placeholders.length > 0; + } +} /** * Configuration for a thorium-locales project. */ class LocaleConfig { - /** - * @param {string} folder - The folder name in thorium-locales - * @param {string} stripPrefix - Prefix to strip from JSON keys - * @param {string} outputPrefix - Prefix to add to output keys - * @param {string} outputFolder - Output folder for .lproj directories - * @param {string[]|null} [includePrefixes] - Optional prefixes to include (if null, include all) - */ - constructor({ folder, stripPrefix, outputPrefix, outputFolder, includePrefixes = null }) { - if (!folder || !stripPrefix || !outputPrefix || !outputFolder) { - throw new Error('LocaleConfig requires folder, stripPrefix, outputPrefix, and outputFolder'); + constructor({ + folder, + stripPrefix = '', + outputPrefix = '', + outputFolder, + includePrefixes = null, + tableName = 'Localizable', + keyTransform = null, + postProcess = null, + convertKeysToCamelCase = true + }) { + if (!folder || !outputFolder) { + throw new Error('LocaleConfig requires folder and outputFolder'); } this.folder = folder; this.stripPrefix = stripPrefix; this.outputPrefix = outputPrefix; this.outputFolder = outputFolder; this.includePrefixes = includePrefixes; + this.tableName = tableName; + this.keyTransform = keyTransform; + this.postProcess = postProcess; + this.convertKeysToCamelCase = convertKeysToCamelCase; } -} -/** - * Configuration for each thorium-locales project. - * Add new projects here as needed. - */ -const PROJECTS = { - lcp: new LocaleConfig({ - folder: 'lcp', - stripPrefix: 'lcp.', - outputPrefix: 'ReadiumLCP.', - outputFolder: 'Sources/LCP/Resources', - includePrefixes: ['lcp.dialog'] - }) -}; + transformEntry(entry) { + const sourceKey = entry.key; + let base = sourceKey.base; -/** - * Languages to process from thorium-locales. - * Only these languages will be included in the output. - */ -const LANGUAGES = ['en', 'fr', 'it']; + if (this.stripPrefix && base.startsWith(this.stripPrefix)) { + base = base.slice(this.stripPrefix.length); + } + if (this.keyTransform) { + base = this.keyTransform(base); + } + if (this.outputPrefix) { + base = this.outputPrefix + base; + } -// Parse arguments -const args = process.argv.slice(2); -const [inputFolder, outputFormat, ...projectNames] = args; + const newKey = new LocalizationKey(base, sourceKey.pluralForm); + return new LocalizationEntry(newKey, entry.value, sourceKey); + } -// If no projects specified, process all configured projects -const projectsToProcess = projectNames.length > 0 - ? projectNames - : Object.keys(PROJECTS); + shouldInclude(entry) { + if (!this.includePrefixes) { + return true; + } + return entry.key.matchesAnyPrefix(this.includePrefixes); + } +} + +// ============================================================================ +// Apple Strings Converter +// ============================================================================ /** - * Ends the script with the given error message. + * Converts localization entries to Apple .strings format. */ -function fail(message) { - console.error(`Error: ${message}`); - process.exit(1); -} +class AppleStringsConverter { + constructor(referenceEntries) { + this._placeholderMappings = this._buildPlaceholderMappings(referenceEntries); + } -async function processLocales() { - for (const projectName of projectsToProcess) { - const config = PROJECTS[projectName]; - if (!config) { - fail(`Unknown project: ${projectName}. Available: ${Object.keys(PROJECTS).join(', ')}`); - } + /** + * Generates .strings file content for the given entries. + */ + generate(lang, entries, config) { + const outputEntries = entries.map(entry => config.transformEntry(entry)); - console.log(`\nProcessing project: ${projectName}`); + const disclaimer = `DO NOT EDIT. File generated automatically from the ${lang} JSON strings of https://github.com/edrlab/thorium-locales/.`; + let content = `// ${disclaimer}\n\n`; - const languageKeys = await loadLanguageKeys(inputFolder, config.folder); - - // Filter keys if includePrefixes is specified - if (config.includePrefixes) { - for (const [lang, keys] of languageKeys) { - const filteredKeys = {}; - for (const [key, value] of Object.entries(keys)) { - // Check if key starts with any of the allowed prefixes - // Also handle plural keys by checking the base key (before @suffix) - const baseKey = getBaseKey(key); - if (config.includePrefixes.some(prefix => baseKey.startsWith(prefix))) { - filteredKeys[key] = value; - } - } - languageKeys.set(lang, filteredKeys); - } + for (const entry of outputEntries) { + content += this._formatEntry(entry, config.convertKeysToCamelCase) + '\n'; } - const placeholderMappings = buildPlaceholderMappings(languageKeys); + // Extract output keys for postProcess + const outputKeys = outputEntries.map(entry => + this._formatKey(entry.key.stripPrefix(config.outputPrefix)) + ); - const writeForProject = (relativePath, content) => { - writeFile(config.outputFolder, relativePath, content); - }; + return { content, outputKeys }; + } - for (const [lang, keys] of languageKeys) { - convert(lang, keys, config, writeForProject, placeholderMappings); + _buildPlaceholderMappings(entries) { + const mappings = new Map(); + for (const entry of entries) { + if (!entry.hasPlaceholders) continue; + const baseKey = entry.key.base; + if (mappings.has(baseKey)) continue; + + const mapping = {}; + entry.placeholders.forEach((name, index) => { + mapping[name] = index + 1; + }); + mappings.set(baseKey, mapping); } + return mappings; } -} -processLocales().catch(err => fail(err.message)); + _getPlaceholderMapping(key) { + return this._placeholderMappings.get(key.base) || {}; + } -/** - * Converter for Apple localized strings. - */ -function convertApple(lang, keys, config, write, placeholderMappings) { - const lproj = `${lang}.lproj`; - // Store both original key (for mapping lookup) and prefixed key (for output) - // Strip the configured prefix since the output prefix already indicates the context - const allEntries = Object.entries(keys).map(([key, value]) => - [key, config.outputPrefix + stripKeyPrefix(key, config.stripPrefix), value] - ); - - // Generate Localizable.strings - write(path.join(lproj, 'Localizable.strings'), generateAppleStrings(lang, allEntries, placeholderMappings)); -} + _formatEntry(entry, convertToCamelCase) { + const transformedKey = convertToCamelCase ? entry.key.toCamelCase() : entry.key; + const outputKey = this._formatKey(transformedKey); + const lookupKey = entry.sourceKey || entry.key; + const mapping = this._getPlaceholderMapping(lookupKey); + const escapedValue = this._escape(entry.value); + const convertedValue = this._convertPlaceholders(escapedValue, mapping); + return `"${outputKey}" = "${convertedValue}";`; + } -/** - * Generates an Apple .strings file content from a list of [originalKey, prefixedKey, value] entries. - */ -function generateAppleStrings(lang, entries, placeholderMappings) { - const disclaimer = `DO NOT EDIT. File generated automatically from the ${lang} JSON strings of https://github.com/edrlab/thorium-locales/.`; - let output = `// ${disclaimer}\n\n`; - for (const [originalKey, prefixedKey, value] of entries) { - // Use original key (without prefix) to look up placeholder mapping - const baseKey = getBaseKey(originalKey); - const mapping = placeholderMappings[originalKey] || placeholderMappings[baseKey] || {}; - const escapedValue = escapeForAppleStrings(value); - const convertedValue = convertPlaceholders(escapedValue, mapping); - output += `"${convertKebabToCamelCase(prefixedKey)}" = "${convertedValue}";\n`; + _formatKey(key) { + if (key.pluralForm) { + return `${key.base}@${key.pluralForm}`; + } + return key.base; } - return output; + _escape(value) { + return value + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/%/g, '%%'); + } + + _convertPlaceholders(value, mapping) { + if (Object.keys(mapping).length === 0) { + return value; + } + return value.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, name) => { + const position = mapping[name]; + if (position === undefined) { + return match; + } + const formatSpec = name === 'count' ? 'd' : '@'; + return `%${position}$${formatSpec}`; + }); + } } -const converters = { - apple: convertApple -}; +// ============================================================================ +// Utility Functions +// ============================================================================ -if (!inputFolder || !outputFormat) { - console.error('Usage: node convert-thorium-localizations.js [project...]'); - console.error(''); - console.error('Arguments:'); - console.error(' input-folder Path to the cloned thorium-locales repository'); - console.error(' output-format Output format (apple)'); - console.error(' project Optional project name(s) to process (default: all)'); - console.error(''); - console.error(`Available projects: ${Object.keys(PROJECTS).join(', ')}`); +function fail(message) { + console.error(`Error: ${message}`); process.exit(1); } -const convert = converters[outputFormat]; -if (!convert) { - fail(`unrecognized output format: ${outputFormat}, try: ${Object.keys(converters).join(', ')}.`); +function writeFile(relativePath, content) { + const outputDir = path.dirname(relativePath); + + try { + fs.mkdirSync(outputDir, { recursive: true }); + fs.writeFileSync(relativePath, content, 'utf8'); + console.log(`Wrote ${relativePath}`); + } catch (err) { + fail(`Failed to write ${relativePath}: ${err.message}`); + } } -/** - * Loads all JSON locale files from the specified folder and returns a Map of language -> keys. - */ -async function loadLanguageKeys(inputFolder, localeFolder) { - const languageKeys = new Map(); +// ============================================================================ +// JSON Parsing +// ============================================================================ + +function parseJsonEntries(obj, prefix = '') { + const entries = []; + + for (const [key, value] of Object.entries(obj)) { + const fullKey = prefix ? `${prefix}.${key}` : key; + + if (typeof value === 'string') { + const pluralSuffix = PLURAL_SUFFIXES.find(suffix => fullKey.endsWith(suffix)); + if (pluralSuffix) { + const baseKey = fullKey.slice(0, -pluralSuffix.length); + const pluralForm = pluralSuffix.slice(1); + entries.push(new LocalizationEntry(new LocalizationKey(baseKey, pluralForm), value)); + } else { + entries.push(new LocalizationEntry(new LocalizationKey(fullKey), value)); + } + } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + entries.push(...parseJsonEntries(value, fullKey)); + } else { + console.warn(`Warning: Skipping unexpected value type for key "${fullKey}": ${typeof value}`); + } + } + + return entries; +} + +async function loadLanguageEntries(inputFolder, localeFolder) { + const languageEntries = new Map(); const folderPath = path.join(inputFolder, localeFolder); if (!fs.existsSync(folderPath)) { @@ -184,202 +299,114 @@ async function loadLanguageKeys(inputFolder, localeFolder) { for (const file of files) { if (path.extname(file) !== '.json') continue; - // Normalize locale to BCP 47 format (hyphens) to merge keys from - // files like pt_PT.json and pt-PT.json into a single locale entry. const lang = path.basename(file, '.json').replace(/_/g, '-'); - - // Skip languages not in the allowed list - if (!LANGUAGES.includes(lang)) { - continue; - } + if (!LANGUAGES.includes(lang)) continue; const filePath = path.join(folderPath, file); try { const data = await fsPromises.readFile(filePath, 'utf8'); const jsonData = JSON.parse(data); - const keys = parseJsonKeys(jsonData); + const entries = parseJsonEntries(jsonData); - if (!languageKeys.has(lang)) { - languageKeys.set(lang, {}); + if (!languageEntries.has(lang)) { + languageEntries.set(lang, []); } - Object.assign(languageKeys.get(lang), keys); + languageEntries.get(lang).push(...entries); } catch (err) { fail(`processing ${file}: ${err.message}`); } } - return languageKeys; + return languageEntries; } -/** - * Builds placeholder-to-position mappings from English keys. - * This ensures consistent argument ordering across all languages. - */ -function buildPlaceholderMappings(languageKeys) { - const placeholderMappings = {}; - const englishKeys = languageKeys.get('en'); - - if (englishKeys) { - for (const [key, value] of Object.entries(englishKeys)) { - const mapping = extractPlaceholderMapping(value); - if (Object.keys(mapping).length > 0) { - // For pluralized keys (ending with @one, @other, etc.), use the base key for mapping - const baseKey = getBaseKey(key); - // Only set if not already set (first plural form encountered sets the mapping) - if (!placeholderMappings[baseKey]) { - placeholderMappings[baseKey] = mapping; - } +// ============================================================================ +// Entry Point +// ============================================================================ + +const PROJECTS = { + lcp: new LocaleConfig({ + folder: 'lcp', + outputPrefix: 'readium.', + outputFolder: 'Sources/LCP/Resources', + includePrefixes: ['lcp.dialog'] + }), + + a11y: new LocaleConfig({ + folder: 'publication-metadata', + stripPrefix: 'publication.metadata.accessibility.display-guide.', + outputPrefix: 'readium.a11y.', + outputFolder: 'Sources/Shared/Resources', + includePrefixes: ['publication.metadata.accessibility.display-guide'], + tableName: 'W3CAccessibilityMetadataDisplayGuide', + keyTransform: key => key.replace(/\./g, '-'), + convertKeysToCamelCase: false, + postProcess: (lang, keys, config) => { + if (lang === 'en') { + generateAccessibilityDisplayStringExtensions( + keys, + 'Sources/Shared/Publication/Accessibility/AccessibilityDisplayString+Generated.swift', + config.outputPrefix, + writeFile + ); } } - } - - return placeholderMappings; -} - -/** - * Writes the given content to the file path relative to the specified base folder. - */ -function writeFile(baseFolder, relativePath, content) { - const outputPath = path.join(baseFolder, relativePath); - const outputDir = path.dirname(outputPath); - - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); - } - - fs.writeFileSync(outputPath, content, 'utf8'); - console.log(`Wrote ${outputPath}`); -} + }) +}; -/** - * Strips the plural suffix from a key (e.g., @one, @other). - */ -function getBaseKey(key) { - return key.replace(/@(zero|one|two|few|many|other)$/, ''); -} +const args = process.argv.slice(2); +const [inputFolder, ...projectNames] = args; -/** - * Strips a prefix from a key. - */ -function stripKeyPrefix(key, prefix) { - return key.startsWith(prefix) ? key.slice(prefix.length) : key; +if (!inputFolder) { + console.error('Usage: node convert-thorium-localizations.js [project...]'); + console.error(''); + console.error('Arguments:'); + console.error(' input-folder Path to the cloned thorium-locales repository'); + console.error(' project Optional project name(s) to process (default: all)'); + console.error(''); + console.error(`Available projects: ${Object.keys(PROJECTS).join(', ')}`); + process.exit(1); } -/** - * Recursively collects the JSON translation keys using dot notation and special handling for pluralization patterns. - * Plural keys use flat format with underscore suffix (e.g., key_one, key_other) which are converted to @ suffix. - */ -function parseJsonKeys(obj, prefix = '') { - const keys = {}; - const pluralSuffixes = ['_zero', '_one', '_two', '_few', '_many', '_other']; - - for (const [key, value] of Object.entries(obj)) { - const fullKey = prefix ? `${prefix}.${key}` : key; +const projectsToProcess = projectNames.length > 0 + ? projectNames + : Object.keys(PROJECTS); - if (typeof value === 'object' && value !== null) { - // Recursively process nested objects - Object.assign(keys, parseJsonKeys(value, fullKey)); - } else { - // Check for plural suffix and convert underscore to @ notation - const pluralSuffix = pluralSuffixes.find(suffix => fullKey.endsWith(suffix)); - if (pluralSuffix) { - const baseKey = fullKey.slice(0, -pluralSuffix.length); - const pluralForm = pluralSuffix.slice(1); // Remove leading underscore - keys[`${baseKey}@${pluralForm}`] = value; - } else { - // Simple key-value pair - keys[fullKey] = value; - } +async function processLocales(inputFolder, projectsToProcess) { + for (const projectName of projectsToProcess) { + const config = PROJECTS[projectName]; + if (!config) { + fail(`Unknown project: ${projectName}. Available: ${Object.keys(PROJECTS).join(', ')}`); } - } - return keys; -} + console.log(`\nProcessing project: ${projectName}`); -function convertKebabToCamelCase(string) { - return string - .split('-') - .map((word, index) => { - if (index === 0) { - return word; - } - return word.charAt(0).toUpperCase() + word.slice(1); - }) - .join(''); -} + const languageEntries = await loadLanguageEntries(inputFolder, config.folder); -/** - * Extracts placeholders from a string and builds a mapping from placeholder name to position index. - * Returns an object with placeholder names as keys and their positional index (1-based) as values. - * Placeholders are assigned positions based on their order of appearance. - */ -function extractPlaceholderMapping(value) { - const placeholderRegex = /\{\{\s*(\w+)\s*\}\}/g; - const placeholders = []; - let match; - - while ((match = placeholderRegex.exec(value)) !== null) { - const name = match[1]; - if (!placeholders.includes(name)) { - placeholders.push(name); + // Filter entries + for (const [lang, entries] of languageEntries) { + const filtered = entries.filter(entry => config.shouldInclude(entry)); + languageEntries.set(lang, filtered); } - } - - if (placeholders.length === 0) { - return {}; - } - - const mapping = {}; - let position = 1; - for (const name of placeholders) { - mapping[name] = position++; - } - return mapping; -} + // Create converter with English entries as reference + const englishEntries = languageEntries.get('en') || []; + const converter = new AppleStringsConverter(englishEntries); -/** - * Escapes special characters for iOS .strings format. - * Must be called before placeholder conversion. - * - * - \ -> \\ - * - " -> \" - * - newlines -> \n - * - % -> %% - */ -function escapeForAppleStrings(value) { - return value - // Escape backslashes first (before other escapes add more backslashes) - .replace(/\\/g, '\\\\') - // Escape double quotes - .replace(/"/g, '\\"') - // Escape newlines - .replace(/\n/g, '\\n') - // Escape literal % characters - .replace(/%/g, '%%'); -} + for (const [lang, entries] of languageEntries) { + const { content, outputKeys } = converter.generate(lang, entries, config); -/** - * Converts Mustache-style placeholders to iOS format specifiers using the provided mapping. - * Placeholders become `%N$@` (string format) or `%N$d` (integer format) for `count`. - * Using %d for `count` allows the GenerateLocalizedUserString.swift script to identify the - * count offset to resolve the pluralization form. - */ -function convertPlaceholders(value, placeholderMap) { - if (Object.keys(placeholderMap).length === 0) { - return value; - } + // Write .strings file + const outputPath = path.join(config.outputFolder, `${lang}.lproj`, `${config.tableName}.strings`); + writeFile(outputPath, content); - return value.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, name) => { - const position = placeholderMap[name]; - if (position === undefined) { - // Placeholder not in mapping, leave unchanged - return match; + // Run postProcess hook + if (config.postProcess) { + config.postProcess(lang, outputKeys, config); + } } - - // Use %d for `count` placeholder, %@ for everything else - const formatSpec = (name === 'count') ? 'd' : '@'; - return `%${position}$${formatSpec}`; - }); + } } + +processLocales(inputFolder, projectsToProcess).catch(err => fail(err.message)); diff --git a/BuildTools/Scripts/generate-a11y-extensions.js b/BuildTools/Scripts/generate-a11y-extensions.js new file mode 100644 index 000000000..8392f5bcc --- /dev/null +++ b/BuildTools/Scripts/generate-a11y-extensions.js @@ -0,0 +1,68 @@ +/** + * Copyright 2026 Readium Foundation. All rights reserved. + * Use of this source code is governed by the BSD-style license + * available in the top-level LICENSE file of the project. + * + * This module generates Swift extensions for AccessibilityDisplayString + * from localization keys. + */ + +/** + * Generates AccessibilityDisplayString Swift extension from localization keys. + * + * @param {string[]} keys - Array of localization keys (without prefix) + * @param {string} outputPath - Path to write the generated Swift file + * @param {string} keyPrefix - Prefix used in localization keys (e.g., "readium.a11y.") + * @param {function} write - Write function (relativePath, content) => void + */ +function generateAccessibilityDisplayStringExtensions(keys, outputPath, keyPrefix, write) { + const disclaimer = 'DO NOT EDIT. File generated automatically from https://github.com/edrlab/thorium-locales/.'; + + // Filter out -descriptive keys (keep base keys only) and remove -compact suffix + const filteredKeys = keys + .filter(k => !k.endsWith('-descriptive')) + .map(k => removeSuffix(k, '-compact')); + + // Remove duplicates (since we removed -compact suffix, some keys may now be the same) + const uniqueKeys = [...new Set(filteredKeys)]; + + let output = `// ${disclaimer} +public extension AccessibilityDisplayString { +`; + + for (const key of uniqueKeys) { + const swiftName = convertKebabToCamelCase(key); + output += ` static let ${swiftName}: Self = "${keyPrefix}${key}"\n`; + } + + output += '}\n'; + + write(outputPath, output); +} + +/** + * Converts a kebab-case string to camelCase. + */ +function convertKebabToCamelCase(string) { + return string + .split('-') + .map((word, index) => { + if (index === 0) { + return word; + } + return word.charAt(0).toUpperCase() + word.slice(1); + }) + .join(''); +} + +/** + * Removes a suffix from a string if present. + */ +function removeSuffix(str, suffix) { + if (str.endsWith(suffix)) { + return str.slice(0, -suffix.length); + } + return str; +} + +module.exports = { generateAccessibilityDisplayStringExtensions }; diff --git a/CHANGELOG.md b/CHANGELOG.md index 896e5ce44..8ae38e5b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ All notable changes to this project will be documented in this file. Take a look * The iOS minimum deployment target is now iOS 15.0. +#### Shared + +* Accessibility display strings are now sourced from the [thorium-locales](https://github.com/edrlab/thorium-locales/) repository (instead of W3C's repository). Contributions are welcome on [Weblate](https://hosted.weblate.org/projects/thorium-reader/publication-metadata/). + #### LCP * The LCP dialog used by `LCPDialogAuthentication` has been redesigned. diff --git a/Makefile b/Makefile index 3365907a2..052ed745f 100644 --- a/Makefile +++ b/Makefile @@ -46,27 +46,17 @@ f: format format: swift run --package-path BuildTools swiftformat . -.PHONY: update-locales -update-locales: update-a11y-locales update-thorium-locales - -.PHONY: update-a11y-locales -update-a11y-locales: - @which node >/dev/null 2>&1 || (echo "ERROR: node is required, please install it first"; exit 1) - rm -rf publ-a11y-display-guide-localizations - git clone https://github.com/w3c/publ-a11y-display-guide-localizations.git - node BuildTools/Scripts/convert-a11y-display-guide-localizations.js publ-a11y-display-guide-localizations apple Sources/Shared readium.a11y. - rm -rf publ-a11y-display-guide-localizations - BRANCH ?= main -.PHONY: update-thorium-locales -update-thorium-locales: +.PHONY: update-locales +update-locales: @which node >/dev/null 2>&1 || (echo "ERROR: node is required, please install it first"; exit 1) ifndef DIR rm -rf thorium-locales git clone -b $(BRANCH) --single-branch --depth 1 https://github.com/edrlab/thorium-locales.git endif - node BuildTools/Scripts/convert-thorium-localizations.js thorium-locales apple + node BuildTools/Scripts/convert-thorium-localizations.js thorium-locales ifndef DIR rm -rf thorium-locales endif + make format diff --git a/Sources/Shared/Publication/Accessibility/AccessibilityDisplayString+Generated.swift b/Sources/Shared/Publication/Accessibility/AccessibilityDisplayString+Generated.swift index 95ee4466f..164ebcfed 100644 --- a/Sources/Shared/Publication/Accessibility/AccessibilityDisplayString+Generated.swift +++ b/Sources/Shared/Publication/Accessibility/AccessibilityDisplayString+Generated.swift @@ -4,24 +4,30 @@ // available in the top-level LICENSE file of the project. // -// DO NOT EDIT. File generated automatically from v2.0.c of the en-US JSON strings. - +// DO NOT EDIT. File generated automatically from https://github.com/edrlab/thorium-locales/. public extension AccessibilityDisplayString { - static let waysOfReadingTitle: Self = "readium.a11y.ways-of-reading-title" - static let waysOfReadingNonvisualReadingAltText: Self = "readium.a11y.ways-of-reading-nonvisual-reading-alt-text" - static let waysOfReadingNonvisualReadingNoMetadata: Self = "readium.a11y.ways-of-reading-nonvisual-reading-no-metadata" - static let waysOfReadingNonvisualReadingNone: Self = "readium.a11y.ways-of-reading-nonvisual-reading-none" - static let waysOfReadingNonvisualReadingNotFully: Self = "readium.a11y.ways-of-reading-nonvisual-reading-not-fully" - static let waysOfReadingNonvisualReadingReadable: Self = "readium.a11y.ways-of-reading-nonvisual-reading-readable" - static let waysOfReadingPrerecordedAudioComplementary: Self = "readium.a11y.ways-of-reading-prerecorded-audio-complementary" - static let waysOfReadingPrerecordedAudioNoMetadata: Self = "readium.a11y.ways-of-reading-prerecorded-audio-no-metadata" - static let waysOfReadingPrerecordedAudioOnly: Self = "readium.a11y.ways-of-reading-prerecorded-audio-only" - static let waysOfReadingPrerecordedAudioSynchronized: Self = "readium.a11y.ways-of-reading-prerecorded-audio-synchronized" - static let waysOfReadingVisualAdjustmentsModifiable: Self = "readium.a11y.ways-of-reading-visual-adjustments-modifiable" - static let waysOfReadingVisualAdjustmentsUnknown: Self = "readium.a11y.ways-of-reading-visual-adjustments-unknown" - static let waysOfReadingVisualAdjustmentsUnmodifiable: Self = "readium.a11y.ways-of-reading-visual-adjustments-unmodifiable" - static let conformanceTitle: Self = "readium.a11y.conformance-title" - static let conformanceDetailsTitle: Self = "readium.a11y.conformance-details-title" + static let accessibilitySummaryNoMetadata: Self = "readium.a11y.accessibility-summary-no-metadata" + static let accessibilitySummaryPublisherContact: Self = "readium.a11y.accessibility-summary-publisher-contact" + static let accessibilitySummaryTitle: Self = "readium.a11y.accessibility-summary-title" + static let additionalAccessibilityInformationAria: Self = "readium.a11y.additional-accessibility-information-aria" + static let additionalAccessibilityInformationAudioDescriptions: Self = "readium.a11y.additional-accessibility-information-audio-descriptions" + static let additionalAccessibilityInformationBraille: Self = "readium.a11y.additional-accessibility-information-braille" + static let additionalAccessibilityInformationColorNotSoleMeansOfConveyingInformation: Self = "readium.a11y.additional-accessibility-information-color-not-sole-means-of-conveying-information" + static let additionalAccessibilityInformationDyslexiaReadability: Self = "readium.a11y.additional-accessibility-information-dyslexia-readability" + static let additionalAccessibilityInformationFullRubyAnnotations: Self = "readium.a11y.additional-accessibility-information-full-ruby-annotations" + static let additionalAccessibilityInformationHighContrastBetweenForegroundAndBackgroundAudio: Self = "readium.a11y.additional-accessibility-information-high-contrast-between-foreground-and-background-audio" + static let additionalAccessibilityInformationHighContrastBetweenTextAndBackground: Self = "readium.a11y.additional-accessibility-information-high-contrast-between-text-and-background" + static let additionalAccessibilityInformationLargePrint: Self = "readium.a11y.additional-accessibility-information-large-print" + static let additionalAccessibilityInformationPageBreaks: Self = "readium.a11y.additional-accessibility-information-page-breaks" + static let additionalAccessibilityInformationRubyAnnotations: Self = "readium.a11y.additional-accessibility-information-ruby-annotations" + static let additionalAccessibilityInformationSignLanguage: Self = "readium.a11y.additional-accessibility-information-sign-language" + static let additionalAccessibilityInformationTactileGraphics: Self = "readium.a11y.additional-accessibility-information-tactile-graphics" + static let additionalAccessibilityInformationTactileObjects: Self = "readium.a11y.additional-accessibility-information-tactile-objects" + static let additionalAccessibilityInformationTextToSpeechHinting: Self = "readium.a11y.additional-accessibility-information-text-to-speech-hinting" + static let additionalAccessibilityInformationTitle: Self = "readium.a11y.additional-accessibility-information-title" + static let additionalAccessibilityInformationUltraHighContrastBetweenTextAndBackground: Self = "readium.a11y.additional-accessibility-information-ultra-high-contrast-between-text-and-background" + static let additionalAccessibilityInformationVisiblePageNumbering: Self = "readium.a11y.additional-accessibility-information-visible-page-numbering" + static let additionalAccessibilityInformationWithoutBackgroundSounds: Self = "readium.a11y.additional-accessibility-information-without-background-sounds" static let conformanceA: Self = "readium.a11y.conformance-a" static let conformanceAa: Self = "readium.a11y.conformance-aa" static let conformanceAaa: Self = "readium.a11y.conformance-aaa" @@ -38,26 +44,10 @@ public extension AccessibilityDisplayString { static let conformanceDetailsWcag20: Self = "readium.a11y.conformance-details-wcag-2-0" static let conformanceDetailsWcag21: Self = "readium.a11y.conformance-details-wcag-2-1" static let conformanceDetailsWcag22: Self = "readium.a11y.conformance-details-wcag-2-2" + static let conformanceDetailsTitle: Self = "readium.a11y.conformance-details-title" static let conformanceNo: Self = "readium.a11y.conformance-no" + static let conformanceTitle: Self = "readium.a11y.conformance-title" static let conformanceUnknownStandard: Self = "readium.a11y.conformance-unknown-standard" - static let navigationTitle: Self = "readium.a11y.navigation-title" - static let navigationIndex: Self = "readium.a11y.navigation-index" - static let navigationNoMetadata: Self = "readium.a11y.navigation-no-metadata" - static let navigationPageNavigation: Self = "readium.a11y.navigation-page-navigation" - static let navigationStructural: Self = "readium.a11y.navigation-structural" - static let navigationToc: Self = "readium.a11y.navigation-toc" - static let richContentTitle: Self = "readium.a11y.rich-content-title" - static let richContentAccessibleChemistryAsLatex: Self = "readium.a11y.rich-content-accessible-chemistry-as-latex" - static let richContentAccessibleChemistryAsMathml: Self = "readium.a11y.rich-content-accessible-chemistry-as-mathml" - static let richContentAccessibleMathAsLatex: Self = "readium.a11y.rich-content-accessible-math-as-latex" - static let richContentAccessibleMathAsMathml: Self = "readium.a11y.rich-content-accessible-math-as-mathml" - static let richContentAccessibleMathDescribed: Self = "readium.a11y.rich-content-accessible-math-described" - static let richContentClosedCaptions: Self = "readium.a11y.rich-content-closed-captions" - static let richContentExtended: Self = "readium.a11y.rich-content-extended" - static let richContentOpenCaptions: Self = "readium.a11y.rich-content-open-captions" - static let richContentTranscript: Self = "readium.a11y.rich-content-transcript" - static let richContentUnknown: Self = "readium.a11y.rich-content-unknown" - static let hazardsTitle: Self = "readium.a11y.hazards-title" static let hazardsFlashing: Self = "readium.a11y.hazards-flashing" static let hazardsFlashingNone: Self = "readium.a11y.hazards-flashing-none" static let hazardsFlashingUnknown: Self = "readium.a11y.hazards-flashing-unknown" @@ -69,30 +59,39 @@ public extension AccessibilityDisplayString { static let hazardsSound: Self = "readium.a11y.hazards-sound" static let hazardsSoundNone: Self = "readium.a11y.hazards-sound-none" static let hazardsSoundUnknown: Self = "readium.a11y.hazards-sound-unknown" + static let hazardsTitle: Self = "readium.a11y.hazards-title" static let hazardsUnknown: Self = "readium.a11y.hazards-unknown" - static let accessibilitySummaryTitle: Self = "readium.a11y.accessibility-summary-title" - static let accessibilitySummaryNoMetadata: Self = "readium.a11y.accessibility-summary-no-metadata" - static let accessibilitySummaryPublisherContact: Self = "readium.a11y.accessibility-summary-publisher-contact" - static let legalConsiderationsTitle: Self = "readium.a11y.legal-considerations-title" static let legalConsiderationsExempt: Self = "readium.a11y.legal-considerations-exempt" static let legalConsiderationsNoMetadata: Self = "readium.a11y.legal-considerations-no-metadata" - static let additionalAccessibilityInformationTitle: Self = "readium.a11y.additional-accessibility-information-title" - static let additionalAccessibilityInformationAria: Self = "readium.a11y.additional-accessibility-information-aria" - static let additionalAccessibilityInformationAudioDescriptions: Self = "readium.a11y.additional-accessibility-information-audio-descriptions" - static let additionalAccessibilityInformationBraille: Self = "readium.a11y.additional-accessibility-information-braille" - static let additionalAccessibilityInformationColorNotSoleMeansOfConveyingInformation: Self = "readium.a11y.additional-accessibility-information-color-not-sole-means-of-conveying-information" - static let additionalAccessibilityInformationDyslexiaReadability: Self = "readium.a11y.additional-accessibility-information-dyslexia-readability" - static let additionalAccessibilityInformationFullRubyAnnotations: Self = "readium.a11y.additional-accessibility-information-full-ruby-annotations" - static let additionalAccessibilityInformationHighContrastBetweenForegroundAndBackgroundAudio: Self = "readium.a11y.additional-accessibility-information-high-contrast-between-foreground-and-background-audio" - static let additionalAccessibilityInformationHighContrastBetweenTextAndBackground: Self = "readium.a11y.additional-accessibility-information-high-contrast-between-text-and-background" - static let additionalAccessibilityInformationLargePrint: Self = "readium.a11y.additional-accessibility-information-large-print" - static let additionalAccessibilityInformationPageBreaks: Self = "readium.a11y.additional-accessibility-information-page-breaks" - static let additionalAccessibilityInformationRubyAnnotations: Self = "readium.a11y.additional-accessibility-information-ruby-annotations" - static let additionalAccessibilityInformationSignLanguage: Self = "readium.a11y.additional-accessibility-information-sign-language" - static let additionalAccessibilityInformationTactileGraphics: Self = "readium.a11y.additional-accessibility-information-tactile-graphics" - static let additionalAccessibilityInformationTactileObjects: Self = "readium.a11y.additional-accessibility-information-tactile-objects" - static let additionalAccessibilityInformationTextToSpeechHinting: Self = "readium.a11y.additional-accessibility-information-text-to-speech-hinting" - static let additionalAccessibilityInformationUltraHighContrastBetweenTextAndBackground: Self = "readium.a11y.additional-accessibility-information-ultra-high-contrast-between-text-and-background" - static let additionalAccessibilityInformationVisiblePageNumbering: Self = "readium.a11y.additional-accessibility-information-visible-page-numbering" - static let additionalAccessibilityInformationWithoutBackgroundSounds: Self = "readium.a11y.additional-accessibility-information-without-background-sounds" + static let legalConsiderationsTitle: Self = "readium.a11y.legal-considerations-title" + static let navigationIndex: Self = "readium.a11y.navigation-index" + static let navigationNoMetadata: Self = "readium.a11y.navigation-no-metadata" + static let navigationPageNavigation: Self = "readium.a11y.navigation-page-navigation" + static let navigationStructural: Self = "readium.a11y.navigation-structural" + static let navigationTitle: Self = "readium.a11y.navigation-title" + static let navigationToc: Self = "readium.a11y.navigation-toc" + static let richContentAccessibleChemistryAsLatex: Self = "readium.a11y.rich-content-accessible-chemistry-as-latex" + static let richContentAccessibleChemistryAsMathml: Self = "readium.a11y.rich-content-accessible-chemistry-as-mathml" + static let richContentAccessibleMathAsLatex: Self = "readium.a11y.rich-content-accessible-math-as-latex" + static let richContentAccessibleMathDescribed: Self = "readium.a11y.rich-content-accessible-math-described" + static let richContentClosedCaptions: Self = "readium.a11y.rich-content-closed-captions" + static let richContentExtendedDescriptions: Self = "readium.a11y.rich-content-extended-descriptions" + static let richContentMathAsMathml: Self = "readium.a11y.rich-content-math-as-mathml" + static let richContentOpenCaptions: Self = "readium.a11y.rich-content-open-captions" + static let richContentTitle: Self = "readium.a11y.rich-content-title" + static let richContentTranscript: Self = "readium.a11y.rich-content-transcript" + static let richContentUnknown: Self = "readium.a11y.rich-content-unknown" + static let waysOfReadingNonvisualReadingAltText: Self = "readium.a11y.ways-of-reading-nonvisual-reading-alt-text" + static let waysOfReadingNonvisualReadingNoMetadata: Self = "readium.a11y.ways-of-reading-nonvisual-reading-no-metadata" + static let waysOfReadingNonvisualReadingNone: Self = "readium.a11y.ways-of-reading-nonvisual-reading-none" + static let waysOfReadingNonvisualReadingNotFully: Self = "readium.a11y.ways-of-reading-nonvisual-reading-not-fully" + static let waysOfReadingNonvisualReadingReadable: Self = "readium.a11y.ways-of-reading-nonvisual-reading-readable" + static let waysOfReadingPrerecordedAudioComplementary: Self = "readium.a11y.ways-of-reading-prerecorded-audio-complementary" + static let waysOfReadingPrerecordedAudioNoMetadata: Self = "readium.a11y.ways-of-reading-prerecorded-audio-no-metadata" + static let waysOfReadingPrerecordedAudioOnly: Self = "readium.a11y.ways-of-reading-prerecorded-audio-only" + static let waysOfReadingPrerecordedAudioSynchronized: Self = "readium.a11y.ways-of-reading-prerecorded-audio-synchronized" + static let waysOfReadingTitle: Self = "readium.a11y.ways-of-reading-title" + static let waysOfReadingVisualAdjustmentsModifiable: Self = "readium.a11y.ways-of-reading-visual-adjustments-modifiable" + static let waysOfReadingVisualAdjustmentsUnknown: Self = "readium.a11y.ways-of-reading-visual-adjustments-unknown" + static let waysOfReadingVisualAdjustmentsUnmodifiable: Self = "readium.a11y.ways-of-reading-visual-adjustments-unmodifiable" } diff --git a/Sources/Shared/Publication/Accessibility/AccessibilityMetadataDisplayGuide.swift b/Sources/Shared/Publication/Accessibility/AccessibilityMetadataDisplayGuide.swift index 3845e61e1..4e05332cf 100644 --- a/Sources/Shared/Publication/Accessibility/AccessibilityMetadataDisplayGuide.swift +++ b/Sources/Shared/Publication/Accessibility/AccessibilityMetadataDisplayGuide.swift @@ -384,13 +384,13 @@ public struct AccessibilityMetadataDisplayGuide: Sendable, Equatable { public var statements: [AccessibilityDisplayStatement] { Array { if extendedAltTextDescriptions { - $0.append(.richContentExtended) + $0.append(.richContentExtendedDescriptions) } if mathFormula { $0.append(.richContentAccessibleMathDescribed) } if mathFormulaAsMathML { - $0.append(.richContentAccessibleMathAsMathml) + $0.append(.richContentMathAsMathml) } if mathFormulaAsLaTeX { $0.append(.richContentAccessibleMathAsLatex) @@ -997,9 +997,14 @@ public struct AccessibilityDisplayString: RawRepresentable, ExpressibleByStringL /// - Parameter descriptive: When true, will return the long descriptive /// statement. public func localized(descriptive: Bool) -> NSAttributedString { - NSAttributedString(string: bundleString("\(rawValue)-\(descriptive ? "descriptive" : "compact")") - .trimmingCharacters(in: .whitespacesAndNewlines) - ) + // Try the suffixed key first, then fall back to the unsuffixed key + // for strings where compact and descriptive variants are identical. + let suffixedKey = "\(rawValue)-\(descriptive ? "descriptive" : "compact")" + var string = bundleString(suffixedKey) + if string == suffixedKey { + string = bundleString(rawValue) + } + return NSAttributedString(string: string.trimmingCharacters(in: .whitespacesAndNewlines)) } private func bundleString(_ key: String, _ values: CVarArg...) -> String { @@ -1024,3 +1029,13 @@ private extension Array where Element == AccessibilityDisplayStatement { append(AccessibilityDisplayStatement(string: string)) } } + +// MARK: - Deprecated Aliases + +public extension AccessibilityDisplayString { + @available(*, deprecated, renamed: "richContentExtendedDescriptions") + static var richContentExtended: Self { richContentExtendedDescriptions } + + @available(*, deprecated, renamed: "richContentMathAsMathml") + static var richContentAccessibleMathAsMathml: Self { richContentMathAsMathml } +} diff --git a/Sources/Shared/Resources/en-US.lproj/W3CAccessibilityMetadataDisplayGuide.strings b/Sources/Shared/Resources/en.lproj/W3CAccessibilityMetadataDisplayGuide.strings similarity index 52% rename from Sources/Shared/Resources/en-US.lproj/W3CAccessibilityMetadataDisplayGuide.strings rename to Sources/Shared/Resources/en.lproj/W3CAccessibilityMetadataDisplayGuide.strings index 1afeade2f..b5b399fad 100644 --- a/Sources/Shared/Resources/en-US.lproj/W3CAccessibilityMetadataDisplayGuide.strings +++ b/Sources/Shared/Resources/en.lproj/W3CAccessibilityMetadataDisplayGuide.strings @@ -1,101 +1,56 @@ -// DO NOT EDIT. File generated automatically from v2.0.c of the en-US JSON strings. +// DO NOT EDIT. File generated automatically from the en JSON strings of https://github.com/edrlab/thorium-locales/. -"readium.a11y.ways-of-reading-title" = "Ways of reading"; -"readium.a11y.ways-of-reading-nonvisual-reading-alt-text-compact" = "Has alternative text"; -"readium.a11y.ways-of-reading-nonvisual-reading-alt-text-descriptive" = "Has alternative text descriptions for images"; -"readium.a11y.ways-of-reading-nonvisual-reading-no-metadata-compact" = "No information about nonvisual reading is available"; -"readium.a11y.ways-of-reading-nonvisual-reading-no-metadata-descriptive" = "No information about nonvisual reading is available"; -"readium.a11y.ways-of-reading-nonvisual-reading-none-compact" = "Not readable in read aloud or dynamic braille"; -"readium.a11y.ways-of-reading-nonvisual-reading-none-descriptive" = "The content is not readable as read aloud speech or dynamic braille"; -"readium.a11y.ways-of-reading-nonvisual-reading-not-fully-compact" = "Not fully readable in read aloud or dynamic braille"; -"readium.a11y.ways-of-reading-nonvisual-reading-not-fully-descriptive" = "Not all of the content will be readable as read aloud speech or dynamic braille"; -"readium.a11y.ways-of-reading-nonvisual-reading-readable-compact" = "Readable in read aloud or dynamic braille"; -"readium.a11y.ways-of-reading-nonvisual-reading-readable-descriptive" = "All content can be read as read aloud speech or dynamic braille"; -"readium.a11y.ways-of-reading-prerecorded-audio-complementary-compact" = "Prerecorded audio clips"; -"readium.a11y.ways-of-reading-prerecorded-audio-complementary-descriptive" = "Prerecorded audio clips are embedded in the content"; -"readium.a11y.ways-of-reading-prerecorded-audio-no-metadata-compact" = "No information about prerecorded audio is available"; -"readium.a11y.ways-of-reading-prerecorded-audio-no-metadata-descriptive" = "No information about prerecorded audio is available"; -"readium.a11y.ways-of-reading-prerecorded-audio-only-compact" = "Prerecorded audio only"; -"readium.a11y.ways-of-reading-prerecorded-audio-only-descriptive" = "Audiobook with no text alternative"; -"readium.a11y.ways-of-reading-prerecorded-audio-synchronized-compact" = "Prerecorded audio synchronized with text"; -"readium.a11y.ways-of-reading-prerecorded-audio-synchronized-descriptive" = "All the content is available as prerecorded audio synchronized with text"; -"readium.a11y.ways-of-reading-visual-adjustments-modifiable-compact" = "Appearance can be modified"; -"readium.a11y.ways-of-reading-visual-adjustments-modifiable-descriptive" = "Appearance of the text and page layout can be modified according to the capabilities of the reading system (font family and font size, spaces between paragraphs, sentences, words, and letters, as well as color of background and text)"; -"readium.a11y.ways-of-reading-visual-adjustments-unknown-compact" = "No information about appearance modifiability is available"; -"readium.a11y.ways-of-reading-visual-adjustments-unknown-descriptive" = "No information about appearance modifiability is available"; -"readium.a11y.ways-of-reading-visual-adjustments-unmodifiable-compact" = "Appearance cannot be modified"; -"readium.a11y.ways-of-reading-visual-adjustments-unmodifiable-descriptive" = "Text and page layout cannot be modified as the reading experience is close to a print version, but reading systems can still provide zooming options"; -"readium.a11y.conformance-title" = "Conformance"; -"readium.a11y.conformance-details-title" = "Detailed conformance information"; +"readium.a11y.accessibility-summary-no-metadata" = "No information is available"; +"readium.a11y.accessibility-summary-publisher-contact" = "For more information about the accessibility of this product, please contact the publisher: "; +"readium.a11y.accessibility-summary-title" = "Accessibility summary"; +"readium.a11y.additional-accessibility-information-aria-compact" = "ARIA roles included"; +"readium.a11y.additional-accessibility-information-aria-descriptive" = "Content is enhanced with ARIA roles to optimize organization and facilitate navigation"; +"readium.a11y.additional-accessibility-information-audio-descriptions" = "Audio descriptions"; +"readium.a11y.additional-accessibility-information-braille" = "Braille"; +"readium.a11y.additional-accessibility-information-color-not-sole-means-of-conveying-information" = "Color is not the sole means of conveying information"; +"readium.a11y.additional-accessibility-information-dyslexia-readability" = "Dyslexia readability"; +"readium.a11y.additional-accessibility-information-full-ruby-annotations" = "Full ruby annotations"; +"readium.a11y.additional-accessibility-information-high-contrast-between-foreground-and-background-audio" = "High contrast between foreground and background audio"; +"readium.a11y.additional-accessibility-information-high-contrast-between-text-and-background" = "High contrast between foreground text and background"; +"readium.a11y.additional-accessibility-information-large-print" = "Large print"; +"readium.a11y.additional-accessibility-information-page-breaks-compact" = "Page breaks included"; +"readium.a11y.additional-accessibility-information-page-breaks-descriptive" = "Page breaks included from the original print source"; +"readium.a11y.additional-accessibility-information-ruby-annotations" = "Some Ruby annotations"; +"readium.a11y.additional-accessibility-information-sign-language" = "Sign language"; +"readium.a11y.additional-accessibility-information-tactile-graphics-compact" = "Tactile graphics included"; +"readium.a11y.additional-accessibility-information-tactile-graphics-descriptive" = "Tactile graphics have been integrated to facilitate access to visual elements for blind people"; +"readium.a11y.additional-accessibility-information-tactile-objects" = "Tactile 3D objects"; +"readium.a11y.additional-accessibility-information-text-to-speech-hinting" = "Text-to-speech hinting provided"; +"readium.a11y.additional-accessibility-information-title" = "Additional accessibility information"; +"readium.a11y.additional-accessibility-information-ultra-high-contrast-between-text-and-background" = "Ultra high contrast between text and background"; +"readium.a11y.additional-accessibility-information-visible-page-numbering" = "Visible page numbering"; +"readium.a11y.additional-accessibility-information-without-background-sounds" = "Without background sounds"; "readium.a11y.conformance-a-compact" = "This publication meets minimum accessibility standards"; "readium.a11y.conformance-a-descriptive" = "The publication contains a conformance statement that it meets the EPUB Accessibility and WCAG 2 Level A standard"; "readium.a11y.conformance-aa-compact" = "This publication meets accepted accessibility standards"; "readium.a11y.conformance-aa-descriptive" = "The publication contains a conformance statement that it meets the EPUB Accessibility and WCAG 2 Level AA standard"; "readium.a11y.conformance-aaa-compact" = "This publication exceeds accepted accessibility standards"; "readium.a11y.conformance-aaa-descriptive" = "The publication contains a conformance statement that it meets the EPUB Accessibility and WCAG 2 Level AAA standard"; -"readium.a11y.conformance-certifier-compact" = "The publication was certified by "; -"readium.a11y.conformance-certifier-descriptive" = "The publication was certified by "; -"readium.a11y.conformance-certifier-credentials-compact" = "The certifier's credential is "; -"readium.a11y.conformance-certifier-credentials-descriptive" = "The certifier's credential is "; -"readium.a11y.conformance-details-certification-info-compact" = "The publication was certified on "; -"readium.a11y.conformance-details-certification-info-descriptive" = "The publication was certified on "; -"readium.a11y.conformance-details-certifier-report-compact" = "For more information refer to the certifier's report"; -"readium.a11y.conformance-details-certifier-report-descriptive" = "For more information refer to the certifier's report"; -"readium.a11y.conformance-details-claim-compact" = "This publication claims to meet"; -"readium.a11y.conformance-details-claim-descriptive" = "This publication claims to meet"; -"readium.a11y.conformance-details-epub-accessibility-1-0-compact" = " EPUB Accessibility 1.0"; -"readium.a11y.conformance-details-epub-accessibility-1-0-descriptive" = " EPUB Accessibility 1.0"; -"readium.a11y.conformance-details-epub-accessibility-1-1-compact" = " EPUB Accessibility 1.1"; -"readium.a11y.conformance-details-epub-accessibility-1-1-descriptive" = " EPUB Accessibility 1.1"; -"readium.a11y.conformance-details-level-a-compact" = " Level A"; -"readium.a11y.conformance-details-level-a-descriptive" = " Level A"; -"readium.a11y.conformance-details-level-aa-compact" = " Level AA"; -"readium.a11y.conformance-details-level-aa-descriptive" = " Level AA"; -"readium.a11y.conformance-details-level-aaa-compact" = " Level AAA"; -"readium.a11y.conformance-details-level-aaa-descriptive" = " Level AAA"; -"readium.a11y.conformance-details-wcag-2-0-compact" = " WCAG 2.0"; -"readium.a11y.conformance-details-wcag-2-0-descriptive" = " Web Content Accessibility Guidelines (WCAG) 2.0"; -"readium.a11y.conformance-details-wcag-2-1-compact" = " WCAG 2.1"; -"readium.a11y.conformance-details-wcag-2-1-descriptive" = " Web Content Accessibility Guidelines (WCAG) 2.1"; -"readium.a11y.conformance-details-wcag-2-2-compact" = " WCAG 2.2"; -"readium.a11y.conformance-details-wcag-2-2-descriptive" = " Web Content Accessibility Guidelines (WCAG) 2.2"; -"readium.a11y.conformance-no-compact" = "No information is available"; -"readium.a11y.conformance-no-descriptive" = "No information is available"; -"readium.a11y.conformance-unknown-standard-compact" = "Conformance to accepted standards for accessibility of this publication cannot be determined"; -"readium.a11y.conformance-unknown-standard-descriptive" = "Conformance to accepted standards for accessibility of this publication cannot be determined"; -"readium.a11y.navigation-title" = "Navigation"; -"readium.a11y.navigation-index-compact" = "Index"; -"readium.a11y.navigation-index-descriptive" = "Index with links to referenced entries"; -"readium.a11y.navigation-no-metadata-compact" = "No information is available"; -"readium.a11y.navigation-no-metadata-descriptive" = "No information is available"; -"readium.a11y.navigation-page-navigation-compact" = "Go to page"; -"readium.a11y.navigation-page-navigation-descriptive" = "Page list to go to pages from the print source version"; -"readium.a11y.navigation-structural-compact" = "Headings"; -"readium.a11y.navigation-structural-descriptive" = "Elements such as headings, tables, etc for structured navigation"; -"readium.a11y.navigation-toc-compact" = "Table of contents"; -"readium.a11y.navigation-toc-descriptive" = "Table of contents to all chapters of the text via links"; -"readium.a11y.rich-content-title" = "Rich content"; -"readium.a11y.rich-content-accessible-chemistry-as-latex-compact" = "Chemical formulas in LaTeX"; -"readium.a11y.rich-content-accessible-chemistry-as-latex-descriptive" = "Chemical formulas in accessible format (LaTeX)"; -"readium.a11y.rich-content-accessible-chemistry-as-mathml-compact" = "Chemical formulas in MathML"; -"readium.a11y.rich-content-accessible-chemistry-as-mathml-descriptive" = "Chemical formulas in accessible format (MathML)"; -"readium.a11y.rich-content-accessible-math-as-latex-compact" = "Math as LaTeX"; -"readium.a11y.rich-content-accessible-math-as-latex-descriptive" = "Math formulas in accessible format (LaTeX)"; -"readium.a11y.rich-content-accessible-math-as-mathml-compact" = "Math as MathML"; -"readium.a11y.rich-content-accessible-math-as-mathml-descriptive" = "Math formulas in accessible format (MathML)"; -"readium.a11y.rich-content-accessible-math-described-compact" = "Text descriptions of math are provided"; -"readium.a11y.rich-content-accessible-math-described-descriptive" = "Text descriptions of math are provided"; -"readium.a11y.rich-content-closed-captions-compact" = "Videos have closed captions"; -"readium.a11y.rich-content-closed-captions-descriptive" = "Videos included in publications have closed captions"; -"readium.a11y.rich-content-extended-compact" = "Information-rich images are described by extended descriptions"; -"readium.a11y.rich-content-extended-descriptive" = "Information-rich images are described by extended descriptions"; -"readium.a11y.rich-content-open-captions-compact" = "Videos have open captions"; -"readium.a11y.rich-content-open-captions-descriptive" = "Videos included in publications have open captions"; -"readium.a11y.rich-content-transcript-compact" = "Transcript(s) provided"; -"readium.a11y.rich-content-transcript-descriptive" = "Transcript(s) provided"; -"readium.a11y.rich-content-unknown-compact" = "No information is available"; -"readium.a11y.rich-content-unknown-descriptive" = "No information is available"; -"readium.a11y.hazards-title" = "Hazards"; +"readium.a11y.conformance-certifier" = "The publication was certified by "; +"readium.a11y.conformance-certifier-credentials" = "The certifier's credential is "; +"readium.a11y.conformance-details-certification-info" = "The publication was certified on "; +"readium.a11y.conformance-details-certifier-report" = "For more information refer to the certifier's report"; +"readium.a11y.conformance-details-claim" = "This publication claims to meet"; +"readium.a11y.conformance-details-epub-accessibility-1-0" = "EPUB Accessibility 1.0"; +"readium.a11y.conformance-details-epub-accessibility-1-1" = "EPUB Accessibility 1.1"; +"readium.a11y.conformance-details-level-a" = "Level A"; +"readium.a11y.conformance-details-level-aa" = "Level AA"; +"readium.a11y.conformance-details-level-aaa" = "Level AAA"; +"readium.a11y.conformance-details-wcag-2-0-compact" = "WCAG 2.0"; +"readium.a11y.conformance-details-wcag-2-0-descriptive" = "Web Content Accessibility Guidelines (WCAG) 2.0"; +"readium.a11y.conformance-details-wcag-2-1-compact" = "WCAG 2.1"; +"readium.a11y.conformance-details-wcag-2-1-descriptive" = "Web Content Accessibility Guidelines (WCAG) 2.1"; +"readium.a11y.conformance-details-wcag-2-2-compact" = "WCAG 2.2"; +"readium.a11y.conformance-details-wcag-2-2-descriptive" = "Web Content Accessibility Guidelines (WCAG) 2.2"; +"readium.a11y.conformance-details-title" = "Detailed conformance information"; +"readium.a11y.conformance-no" = "No information is available"; +"readium.a11y.conformance-title" = "Conformance"; +"readium.a11y.conformance-unknown-standard" = "Conformance to accepted standards for accessibility of this publication cannot be determined"; "readium.a11y.hazards-flashing-compact" = "Flashing content"; "readium.a11y.hazards-flashing-descriptive" = "The publication contains flashing content that can cause photosensitive seizures"; "readium.a11y.hazards-flashing-none-compact" = "No flashing hazards"; @@ -108,8 +63,7 @@ "readium.a11y.hazards-motion-none-descriptive" = "The publication does not contain motion simulations that can cause motion sickness"; "readium.a11y.hazards-motion-unknown-compact" = "Motion simulation hazards not known"; "readium.a11y.hazards-motion-unknown-descriptive" = "The presence of motion simulations that can cause motion sickness could not be determined"; -"readium.a11y.hazards-no-metadata-compact" = "No information is available"; -"readium.a11y.hazards-no-metadata-descriptive" = "No information is available"; +"readium.a11y.hazards-no-metadata" = "No information is available"; "readium.a11y.hazards-none-compact" = "No hazards"; "readium.a11y.hazards-none-descriptive" = "The publication contains no hazards"; "readium.a11y.hazards-sound-compact" = "Sounds"; @@ -118,52 +72,58 @@ "readium.a11y.hazards-sound-none-descriptive" = "The publication does not contain sounds that can cause sensitivity issues"; "readium.a11y.hazards-sound-unknown-compact" = "Sound hazards not known"; "readium.a11y.hazards-sound-unknown-descriptive" = "The presence of sounds that can cause sensitivity issues could not be determined"; -"readium.a11y.hazards-unknown-compact" = "The presence of hazards is unknown"; -"readium.a11y.hazards-unknown-descriptive" = "The presence of hazards is unknown"; -"readium.a11y.accessibility-summary-title" = "Accessibility summary"; -"readium.a11y.accessibility-summary-no-metadata-compact" = "No information is available"; -"readium.a11y.accessibility-summary-no-metadata-descriptive" = "No information is available"; -"readium.a11y.accessibility-summary-publisher-contact-compact" = "For more information about the accessibility of this product, please contact the publisher: "; -"readium.a11y.accessibility-summary-publisher-contact-descriptive" = "For more information about the accessibility of this product, please contact the publisher: "; -"readium.a11y.legal-considerations-title" = "Legal considerations"; +"readium.a11y.hazards-title" = "Hazards"; +"readium.a11y.hazards-unknown" = "The presence of hazards is unknown"; "readium.a11y.legal-considerations-exempt-compact" = "Claims an accessibility exemption in some jurisdictions"; "readium.a11y.legal-considerations-exempt-descriptive" = "This publication claims an accessibility exemption in some jurisdictions"; -"readium.a11y.legal-considerations-no-metadata-compact" = "No information is available"; -"readium.a11y.legal-considerations-no-metadata-descriptive" = "No information is available"; -"readium.a11y.additional-accessibility-information-title" = "Additional accessibility information"; -"readium.a11y.additional-accessibility-information-aria-compact" = "ARIA roles included"; -"readium.a11y.additional-accessibility-information-aria-descriptive" = "Content is enhanced with ARIA roles to optimize organization and facilitate navigation"; -"readium.a11y.additional-accessibility-information-audio-descriptions-compact" = "Audio descriptions"; -"readium.a11y.additional-accessibility-information-audio-descriptions-descriptive" = "Audio descriptions"; -"readium.a11y.additional-accessibility-information-braille-compact" = "Braille"; -"readium.a11y.additional-accessibility-information-braille-descriptive" = "Braille"; -"readium.a11y.additional-accessibility-information-color-not-sole-means-of-conveying-information-compact" = "Color is not the sole means of conveying information"; -"readium.a11y.additional-accessibility-information-color-not-sole-means-of-conveying-information-descriptive" = "Color is not the sole means of conveying information"; -"readium.a11y.additional-accessibility-information-dyslexia-readability-compact" = "Dyslexia readability"; -"readium.a11y.additional-accessibility-information-dyslexia-readability-descriptive" = "Dyslexia readability"; -"readium.a11y.additional-accessibility-information-full-ruby-annotations-compact" = "Full ruby annotations"; -"readium.a11y.additional-accessibility-information-full-ruby-annotations-descriptive" = "Full ruby annotations"; -"readium.a11y.additional-accessibility-information-high-contrast-between-foreground-and-background-audio-compact" = "High contrast between foreground and background audio"; -"readium.a11y.additional-accessibility-information-high-contrast-between-foreground-and-background-audio-descriptive" = "High contrast between foreground and background audio"; -"readium.a11y.additional-accessibility-information-high-contrast-between-text-and-background-compact" = "High contrast between foreground text and background"; -"readium.a11y.additional-accessibility-information-high-contrast-between-text-and-background-descriptive" = "High contrast between foreground text and background"; -"readium.a11y.additional-accessibility-information-large-print-compact" = "Large print"; -"readium.a11y.additional-accessibility-information-large-print-descriptive" = "Large print"; -"readium.a11y.additional-accessibility-information-page-breaks-compact" = "Page breaks included"; -"readium.a11y.additional-accessibility-information-page-breaks-descriptive" = "Page breaks included from the original print source"; -"readium.a11y.additional-accessibility-information-ruby-annotations-compact" = "Some Ruby annotations"; -"readium.a11y.additional-accessibility-information-ruby-annotations-descriptive" = "Some Ruby annotations"; -"readium.a11y.additional-accessibility-information-sign-language-compact" = "Sign language"; -"readium.a11y.additional-accessibility-information-sign-language-descriptive" = "Sign language"; -"readium.a11y.additional-accessibility-information-tactile-graphics-compact" = "Tactile graphics included"; -"readium.a11y.additional-accessibility-information-tactile-graphics-descriptive" = "Tactile graphics have been integrated to facilitate access to visual elements for blind people"; -"readium.a11y.additional-accessibility-information-tactile-objects-compact" = "Tactile 3D objects"; -"readium.a11y.additional-accessibility-information-tactile-objects-descriptive" = "Tactile 3D objects"; -"readium.a11y.additional-accessibility-information-text-to-speech-hinting-compact" = "Text-to-speech hinting provided"; -"readium.a11y.additional-accessibility-information-text-to-speech-hinting-descriptive" = "Text-to-speech hinting provided"; -"readium.a11y.additional-accessibility-information-ultra-high-contrast-between-text-and-background-compact" = "Ultra high contrast between text and background"; -"readium.a11y.additional-accessibility-information-ultra-high-contrast-between-text-and-background-descriptive" = "Ultra high contrast between text and background"; -"readium.a11y.additional-accessibility-information-visible-page-numbering-compact" = "Visible page numbering"; -"readium.a11y.additional-accessibility-information-visible-page-numbering-descriptive" = "Visible page numbering"; -"readium.a11y.additional-accessibility-information-without-background-sounds-compact" = "Without background sounds"; -"readium.a11y.additional-accessibility-information-without-background-sounds-descriptive" = "Without background sounds"; +"readium.a11y.legal-considerations-no-metadata" = "No information is available"; +"readium.a11y.legal-considerations-title" = "Legal considerations"; +"readium.a11y.navigation-index-compact" = "Index"; +"readium.a11y.navigation-index-descriptive" = "Index with links to referenced entries"; +"readium.a11y.navigation-no-metadata" = "No information is available"; +"readium.a11y.navigation-page-navigation-compact" = "Go to page"; +"readium.a11y.navigation-page-navigation-descriptive" = "Page list to go to pages from the print source version"; +"readium.a11y.navigation-structural-compact" = "Headings"; +"readium.a11y.navigation-structural-descriptive" = "Elements such as headings, tables, etc for structured navigation"; +"readium.a11y.navigation-title" = "Navigation"; +"readium.a11y.navigation-toc-compact" = "Table of contents"; +"readium.a11y.navigation-toc-descriptive" = "Table of contents to all chapters of the text via links"; +"readium.a11y.rich-content-accessible-chemistry-as-latex-compact" = "Chemical formulas in LaTeX"; +"readium.a11y.rich-content-accessible-chemistry-as-latex-descriptive" = "Chemical formulas in accessible format (LaTeX)"; +"readium.a11y.rich-content-accessible-chemistry-as-mathml-compact" = "Chemical formulas in MathML"; +"readium.a11y.rich-content-accessible-chemistry-as-mathml-descriptive" = "Chemical formulas in accessible format (MathML)"; +"readium.a11y.rich-content-accessible-math-as-latex-compact" = "Math as LaTeX"; +"readium.a11y.rich-content-accessible-math-as-latex-descriptive" = "Math formulas in accessible format (LaTeX)"; +"readium.a11y.rich-content-accessible-math-described" = "Text descriptions of math are provided"; +"readium.a11y.rich-content-closed-captions-compact" = "Videos have closed captions"; +"readium.a11y.rich-content-closed-captions-descriptive" = "Videos included in publications have closed captions"; +"readium.a11y.rich-content-extended-descriptions" = "Information-rich images are described by extended descriptions"; +"readium.a11y.rich-content-math-as-mathml-compact" = "Math as MathML"; +"readium.a11y.rich-content-math-as-mathml-descriptive" = "Math formulas in accessible format (MathML)"; +"readium.a11y.rich-content-open-captions-compact" = "Videos have open captions"; +"readium.a11y.rich-content-open-captions-descriptive" = "Videos included in publications have open captions"; +"readium.a11y.rich-content-title" = "Rich content"; +"readium.a11y.rich-content-transcript" = "Transcript(s) provided"; +"readium.a11y.rich-content-unknown" = "No information is available"; +"readium.a11y.ways-of-reading-nonvisual-reading-alt-text-compact" = "Has alternative text"; +"readium.a11y.ways-of-reading-nonvisual-reading-alt-text-descriptive" = "Has alternative text descriptions for images"; +"readium.a11y.ways-of-reading-nonvisual-reading-no-metadata" = "No information about nonvisual reading is available"; +"readium.a11y.ways-of-reading-nonvisual-reading-none-compact" = "Not readable in read aloud or dynamic braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-none-descriptive" = "The content is not readable as read aloud speech or dynamic braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-not-fully-compact" = "Not fully readable in read aloud or dynamic braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-not-fully-descriptive" = "Not all of the content will be readable as read aloud speech or dynamic braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-readable-compact" = "Readable in read aloud or dynamic braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-readable-descriptive" = "All content can be read as read aloud speech or dynamic braille"; +"readium.a11y.ways-of-reading-prerecorded-audio-complementary-compact" = "Prerecorded audio clips"; +"readium.a11y.ways-of-reading-prerecorded-audio-complementary-descriptive" = "Prerecorded audio clips are embedded in the content"; +"readium.a11y.ways-of-reading-prerecorded-audio-no-metadata" = "No information about prerecorded audio is available"; +"readium.a11y.ways-of-reading-prerecorded-audio-only-compact" = "Prerecorded audio only"; +"readium.a11y.ways-of-reading-prerecorded-audio-only-descriptive" = "Audiobook with no text alternative"; +"readium.a11y.ways-of-reading-prerecorded-audio-synchronized-compact" = "Prerecorded audio synchronized with text"; +"readium.a11y.ways-of-reading-prerecorded-audio-synchronized-descriptive" = "All the content is available as prerecorded audio synchronized with text"; +"readium.a11y.ways-of-reading-title" = "Ways of reading"; +"readium.a11y.ways-of-reading-visual-adjustments-modifiable-compact" = "Appearance can be modified"; +"readium.a11y.ways-of-reading-visual-adjustments-modifiable-descriptive" = "Appearance of the text and page layout can be modified according to the capabilities of the reading system (font family and font size, spaces between paragraphs, sentences, words, and letters, as well as color of background and text)"; +"readium.a11y.ways-of-reading-visual-adjustments-unknown" = "No information about appearance modifiability is available"; +"readium.a11y.ways-of-reading-visual-adjustments-unmodifiable-compact" = "Appearance cannot be modified"; +"readium.a11y.ways-of-reading-visual-adjustments-unmodifiable-descriptive" = "Text and page layout cannot be modified as the reading experience is close to a print version, but reading systems can still provide zooming options"; diff --git a/Sources/Shared/Resources/fr.lproj/W3CAccessibilityMetadataDisplayGuide.strings b/Sources/Shared/Resources/fr.lproj/W3CAccessibilityMetadataDisplayGuide.strings new file mode 100644 index 000000000..44f03cd8e --- /dev/null +++ b/Sources/Shared/Resources/fr.lproj/W3CAccessibilityMetadataDisplayGuide.strings @@ -0,0 +1,129 @@ +// DO NOT EDIT. File generated automatically from the fr JSON strings of https://github.com/edrlab/thorium-locales/. + +"readium.a11y.accessibility-summary-no-metadata" = "Aucune information disponible"; +"readium.a11y.accessibility-summary-publisher-contact" = "Pour plus d'information à propos de l'accessibilité de cette publication, veuillez contacter l'éditeur : "; +"readium.a11y.accessibility-summary-title" = "Informations d'accessibilité supplémentaires fournies par l'éditeur"; +"readium.a11y.additional-accessibility-information-aria-compact" = "Information enrichie pour les technologies d'assistances"; +"readium.a11y.additional-accessibility-information-aria-descriptive" = "La structure est enrichi de rôles ARIA afin d'optimiser l'organisation et de faciliter la navigation via les technologies d'assistances"; +"readium.a11y.additional-accessibility-information-audio-descriptions" = "Description audio"; +"readium.a11y.additional-accessibility-information-braille" = "Braille"; +"readium.a11y.additional-accessibility-information-color-not-sole-means-of-conveying-information" = "La couleur n'est pas la seule manière de communiquer de l'information"; +"readium.a11y.additional-accessibility-information-dyslexia-readability" = "Lisibilité adapté aux publics dys"; +"readium.a11y.additional-accessibility-information-full-ruby-annotations" = "Annotations complètes au format ruby (langues asiatiques)"; +"readium.a11y.additional-accessibility-information-high-contrast-between-foreground-and-background-audio" = "Contraste sonore amélioré entre les différents plans"; +"readium.a11y.additional-accessibility-information-high-contrast-between-text-and-background" = "Contraste élevé entre le texte et l'arrière-plan"; +"readium.a11y.additional-accessibility-information-large-print" = "Grands caractères"; +"readium.a11y.additional-accessibility-information-page-breaks-compact" = "Pagination identique à l'imprimé"; +"readium.a11y.additional-accessibility-information-page-breaks-descriptive" = "Contient une pagination identique à la version imprimée"; +"readium.a11y.additional-accessibility-information-ruby-annotations" = "Annotations partielles au format ruby (langues asiatiques)"; +"readium.a11y.additional-accessibility-information-sign-language" = "Langue des signes"; +"readium.a11y.additional-accessibility-information-tactile-graphics-compact" = "Graphiques tactiles"; +"readium.a11y.additional-accessibility-information-tactile-graphics-descriptive" = "Des graphiques tactiles ont été intégrés pour faciliter l'accès des personnes aveugles aux éléments visuels"; +"readium.a11y.additional-accessibility-information-tactile-objects" = "Objets 3D ou tactiles"; +"readium.a11y.additional-accessibility-information-text-to-speech-hinting" = "Prononciation améliorée pour la synthèse vocale"; +"readium.a11y.additional-accessibility-information-title" = "Informations complémentaires sur l'accessibilité"; +"readium.a11y.additional-accessibility-information-ultra-high-contrast-between-text-and-background" = "Contraste très élevé entre le texte et l'arrière-plan"; +"readium.a11y.additional-accessibility-information-visible-page-numbering" = "Numérotation de page visible"; +"readium.a11y.additional-accessibility-information-without-background-sounds" = "Aucun bruit de fond"; +"readium.a11y.conformance-a-compact" = "Cette publication répond aux règles minimales d'accessibilité"; +"readium.a11y.conformance-a-descriptive" = "La publication indique qu'elle respecte les règles d'accessibilité EPUB et WCAG 2 niveau A"; +"readium.a11y.conformance-aa-compact" = "Cette publication répond aux règles d'accessibilité reconnues"; +"readium.a11y.conformance-aa-descriptive" = "La publication indique qu'elle respecte les règles d'accessibilité EPUB et WCAG 2 niveau AA"; +"readium.a11y.conformance-aaa-compact" = "Cette publication dépasse les règles d'accessibilité reconnues"; +"readium.a11y.conformance-aaa-descriptive" = "La publication indique qu'elle respecte les règles d'accessibilité EPUB et WCAG 2 niveau AAA"; +"readium.a11y.conformance-certifier" = "Accessibilité évaluée par "; +"readium.a11y.conformance-certifier-credentials" = "L'évaluateur est accrédité par "; +"readium.a11y.conformance-details-certification-info" = "Cette publication a été certifié le"; +"readium.a11y.conformance-details-certifier-report" = "Pour plus d'information, veuillez consulter le rapport de certification"; +"readium.a11y.conformance-details-claim" = "Cette publication indique respecter"; +"readium.a11y.conformance-details-epub-accessibility-1-0" = "EPUB Accessibilité 1.0"; +"readium.a11y.conformance-details-epub-accessibility-1-1" = "EPUB Accessibilité 1.1"; +"readium.a11y.conformance-details-level-a" = "Niveau A"; +"readium.a11y.conformance-details-level-aa" = "Niveau AA"; +"readium.a11y.conformance-details-level-aaa" = "Niveau AAA"; +"readium.a11y.conformance-details-wcag-2-0-compact" = "WCAG 2.0"; +"readium.a11y.conformance-details-wcag-2-0-descriptive" = "Règles pour l’accessibilité des contenus Web (WCAG) 2.0"; +"readium.a11y.conformance-details-wcag-2-1-compact" = "WCAG 2.1"; +"readium.a11y.conformance-details-wcag-2-1-descriptive" = "Règles pour l’accessibilité des contenus Web (WCAG) 2.1"; +"readium.a11y.conformance-details-wcag-2-2-compact" = "WCAG 2.2"; +"readium.a11y.conformance-details-wcag-2-2-descriptive" = "Règles pour l’accessibilité des contenus Web (WCAG) 2.2"; +"readium.a11y.conformance-details-title" = "Information détaillée"; +"readium.a11y.conformance-no" = "Aucune information disponible"; +"readium.a11y.conformance-title" = "Règles d'accessibilité"; +"readium.a11y.conformance-unknown-standard" = "Aucune indication concernant les normes d'accessibilité"; +"readium.a11y.hazards-flashing-compact" = "Flashs lumineux"; +"readium.a11y.hazards-flashing-descriptive" = "La publication contient des flashs lumineux qui peuvent provoquer des crises d’épilepsie"; +"readium.a11y.hazards-flashing-none-compact" = "Pas de flashs lumineux"; +"readium.a11y.hazards-flashing-none-descriptive" = "La publication ne contient pas de flashs lumineux susceptibles de provoquer des crises d’épilepsie"; +"readium.a11y.hazards-flashing-unknown-compact" = "Pas d'information concernant la présence de flashs lumineux"; +"readium.a11y.hazards-flashing-unknown-descriptive" = "La présence de flashs lumineux susceptibles de provoquer des crises d’épilepsie n'a pas pu être déterminée"; +"readium.a11y.hazards-motion-compact" = "Sensations de mouvement"; +"readium.a11y.hazards-motion-descriptive" = "La publication contient des images en mouvement qui peuvent provoquer des nausées, des vertiges et des maux de tête"; +"readium.a11y.hazards-motion-none-compact" = "Pas de sensations de mouvement"; +"readium.a11y.hazards-motion-none-descriptive" = "La publication ne contient pas d'images en mouvement qui pourraient provoquer des nausées, des vertiges et des maux de tête"; +"readium.a11y.hazards-motion-unknown-compact" = "Pas d'information concernant la présence d'images en mouvement"; +"readium.a11y.hazards-motion-unknown-descriptive" = "La présence d'images en mouvement susceptibles de provoquer des nausées, des vertiges et des maux de tête n'a pas pu être déterminée"; +"readium.a11y.hazards-no-metadata" = "Aucune information disponible"; +"readium.a11y.hazards-none-compact" = "Aucun points d'attention"; +"readium.a11y.hazards-none-descriptive" = "La publication ne présente aucun risque lié à la présence de flashs lumineux, de sensations de mouvement ou de sons"; +"readium.a11y.hazards-sound-compact" = "Sons"; +"readium.a11y.hazards-sound-descriptive" = "La publication contient des sons qui peuvent causer des troubles de la sensibilité"; +"readium.a11y.hazards-sound-none-compact" = "Pas de risques sonores"; +"readium.a11y.hazards-sound-none-descriptive" = "La publication ne contient pas de sons susceptibles de provoquer des troubles de la sensibilité"; +"readium.a11y.hazards-sound-unknown-compact" = "Pas d'information concernant la présence de sons"; +"readium.a11y.hazards-sound-unknown-descriptive" = "La présence de sons susceptibles de causer des troubles de sensibilité n'a pas pu être déterminée"; +"readium.a11y.hazards-title" = "Points d'attention"; +"readium.a11y.hazards-unknown" = "La présence de risques est inconnue"; +"readium.a11y.legal-considerations-exempt-compact" = "Déclare être sous le coup d'une exemption dans certaines juridictions"; +"readium.a11y.legal-considerations-exempt-descriptive" = "Cette publication dééclare être sous le coup d'une exemption dans certaines juridictions"; +"readium.a11y.legal-considerations-no-metadata" = "Aucune information disponible"; +"readium.a11y.legal-considerations-title" = "Considérations légales"; +"readium.a11y.navigation-index-compact" = "Index"; +"readium.a11y.navigation-index-descriptive" = "Index comportant des liens vers les entrées référencées"; +"readium.a11y.navigation-no-metadata" = "Aucune information disponible"; +"readium.a11y.navigation-page-navigation-compact" = "Aller à la page"; +"readium.a11y.navigation-page-navigation-descriptive" = "Permet d'accéder aux pages de la version source imprimée"; +"readium.a11y.navigation-structural-compact" = "Titres"; +"readium.a11y.navigation-structural-descriptive" = "Contient des titres pour une navigation structurée"; +"readium.a11y.navigation-title" = "Points de repère"; +"readium.a11y.navigation-toc-compact" = "Table des matières"; +"readium.a11y.navigation-toc-descriptive" = "Table des matières"; +"readium.a11y.rich-content-accessible-chemistry-as-latex-compact" = "Formules chimiques en LaTeX"; +"readium.a11y.rich-content-accessible-chemistry-as-latex-descriptive" = "Formules chimiques en format accessible (LaTeX)"; +"readium.a11y.rich-content-accessible-chemistry-as-mathml-compact" = "Formules chimiques en MathML"; +"readium.a11y.rich-content-accessible-chemistry-as-mathml-descriptive" = "Formules chimiques en format accessible (MathML)"; +"readium.a11y.rich-content-accessible-math-as-latex-compact" = "Mathématiques en LaTeX"; +"readium.a11y.rich-content-accessible-math-as-latex-descriptive" = "Formules mathématiques en format accessible (LaTeX)"; +"readium.a11y.rich-content-accessible-math-described" = "Des descriptions textuelles des formules mathématiques sont fournies"; +"readium.a11y.rich-content-closed-captions-compact" = "Sous-titres disponibles pour les vidéos"; +"readium.a11y.rich-content-closed-captions-descriptive" = "Des sous titres sont disponibles pour les vidéos"; +"readium.a11y.rich-content-extended-descriptions" = "Les images porteuses d'informations complexes sont décrites par des descriptions longues"; +"readium.a11y.rich-content-math-as-mathml-compact" = "Mathématiques en MathML"; +"readium.a11y.rich-content-math-as-mathml-descriptive" = "Formules mathématiques en format accessible (MathML)"; +"readium.a11y.rich-content-open-captions-compact" = "Sous-titres incrustés"; +"readium.a11y.rich-content-open-captions-descriptive" = "Des sous titres sont incrustés pour les vidéos"; +"readium.a11y.rich-content-title" = "Contenus spécifiques"; +"readium.a11y.rich-content-transcript" = "Transcriptions fournies"; +"readium.a11y.rich-content-unknown" = "Aucune information disponible"; +"readium.a11y.ways-of-reading-nonvisual-reading-alt-text-compact" = "Images décrites"; +"readium.a11y.ways-of-reading-nonvisual-reading-alt-text-descriptive" = "Les images sont décrites par un texte"; +"readium.a11y.ways-of-reading-nonvisual-reading-no-metadata" = "Aucune information pour la lecture en voix de synthèse ou en braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-none-compact" = "Non lisible en voix de synthèse ou en braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-none-descriptive" = "Le contenu n'est pas lisible en voix de synthèse ou en braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-not-fully-compact" = "Pas entièrement lisible en voix de synthèse ou en braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-not-fully-descriptive" = "Tous les contenus ne pourront pas être lus à haute voix ou en braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-readable-compact" = "Entièrement lisible en voix de synthèse ou en braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-readable-descriptive" = "Tous les contenus peuvent être lus en voix de synthèse ou en braille"; +"readium.a11y.ways-of-reading-prerecorded-audio-complementary-compact" = "Clips audio préenregistrés"; +"readium.a11y.ways-of-reading-prerecorded-audio-complementary-descriptive" = "Des clips audio préenregistrés sont intégrés au contenu"; +"readium.a11y.ways-of-reading-prerecorded-audio-no-metadata" = "Aucune information sur les enregistrements audio"; +"readium.a11y.ways-of-reading-prerecorded-audio-only-compact" = "Audio préenregistré uniquement"; +"readium.a11y.ways-of-reading-prerecorded-audio-only-descriptive" = "Livre audio sans texte alternatif"; +"readium.a11y.ways-of-reading-prerecorded-audio-synchronized-compact" = "Audio préenregistré synchronisé avec du texte"; +"readium.a11y.ways-of-reading-prerecorded-audio-synchronized-descriptive" = "Tous les contenus sont disponibles comme audio préenregistrés synchronisés avec le texte"; +"readium.a11y.ways-of-reading-title" = "Lisibilité"; +"readium.a11y.ways-of-reading-visual-adjustments-modifiable-compact" = "L'affichage peut être adapté"; +"readium.a11y.ways-of-reading-visual-adjustments-modifiable-descriptive" = "L'apparence du texte et la mise en page peuvent être modifiées en fonction des capacités du système de lecture (famille et taille des polices, espaces entre les paragraphes, les phrases, les mots et les lettres, ainsi que la couleur de l'arrière-plan et du texte)"; +"readium.a11y.ways-of-reading-visual-adjustments-unknown" = "Aucune information sur les possibilités d'adaptation de l'affichage"; +"readium.a11y.ways-of-reading-visual-adjustments-unmodifiable-compact" = "L'affichage ne peut pas être adapté"; +"readium.a11y.ways-of-reading-visual-adjustments-unmodifiable-descriptive" = "Le texte et la mise en page ne peuvent pas être adaptés étant donné que l'expérience de lecture est proche de celle de la version imprimée, mais l'application de lecture peut tout de même proposer la capacité de zoomer"; diff --git a/Sources/Shared/Resources/it.lproj/W3CAccessibilityMetadataDisplayGuide.strings b/Sources/Shared/Resources/it.lproj/W3CAccessibilityMetadataDisplayGuide.strings new file mode 100644 index 000000000..76133ad4f --- /dev/null +++ b/Sources/Shared/Resources/it.lproj/W3CAccessibilityMetadataDisplayGuide.strings @@ -0,0 +1,129 @@ +// DO NOT EDIT. File generated automatically from the it JSON strings of https://github.com/edrlab/thorium-locales/. + +"readium.a11y.accessibility-summary-no-metadata" = "Nessuna informazione disponibile"; +"readium.a11y.accessibility-summary-publisher-contact" = "Per ulteriori informazioni sull'accessibilità di questa risorsa, contattare l'editore: "; +"readium.a11y.accessibility-summary-title" = "Informazioni aggiuntive sull'accessibilità fornite dall'editore"; +"readium.a11y.additional-accessibility-information-aria-compact" = "Ruoli ARIA inclusi"; +"readium.a11y.additional-accessibility-information-aria-descriptive" = "Il contenuto è semanticamente arricchito con ruoli ARIA per ottimizzare l'organizzazione e facilitare la navigazione"; +"readium.a11y.additional-accessibility-information-audio-descriptions" = "Descrizioni audio"; +"readium.a11y.additional-accessibility-information-braille" = "Braille"; +"readium.a11y.additional-accessibility-information-color-not-sole-means-of-conveying-information" = "Il colore non è l'unico mezzo per trasmettere informazioni"; +"readium.a11y.additional-accessibility-information-dyslexia-readability" = "Leggibilità adatta alla dislessia"; +"readium.a11y.additional-accessibility-information-full-ruby-annotations" = "Annotazioni complete in Ruby"; +"readium.a11y.additional-accessibility-information-high-contrast-between-foreground-and-background-audio" = "Elevato contrasto tra audio principale e sottofondo"; +"readium.a11y.additional-accessibility-information-high-contrast-between-text-and-background" = "Contrasto elevato tra testo in primo piano e sfondo"; +"readium.a11y.additional-accessibility-information-large-print" = "Stampa a caratteri ingranditi"; +"readium.a11y.additional-accessibility-information-page-breaks-compact" = "Interruzioni di pagina incluse"; +"readium.a11y.additional-accessibility-information-page-breaks-descriptive" = "Interruzioni di pagina identiche alla versione originale a stampa"; +"readium.a11y.additional-accessibility-information-ruby-annotations" = "Alcune annotazioni in Ruby"; +"readium.a11y.additional-accessibility-information-sign-language" = "Lingua dei segni"; +"readium.a11y.additional-accessibility-information-tactile-graphics-compact" = "Grafica tattile inclusa"; +"readium.a11y.additional-accessibility-information-tactile-graphics-descriptive" = "La grafica tattile è stata integrata per facilitare l'accesso agli elementi visivi alle persone non vedenti"; +"readium.a11y.additional-accessibility-information-tactile-objects" = "Oggetti 3D tattili"; +"readium.a11y.additional-accessibility-information-text-to-speech-hinting" = "Pronuncia migliorata per la sintesi vocale"; +"readium.a11y.additional-accessibility-information-title" = "Ulteriori informazioni sull'accessibilità"; +"readium.a11y.additional-accessibility-information-ultra-high-contrast-between-text-and-background" = "Contrasto molto elevato tra testo e sfondo"; +"readium.a11y.additional-accessibility-information-visible-page-numbering" = "Numerazione delle pagine visibile"; +"readium.a11y.additional-accessibility-information-without-background-sounds" = "Nessun suono in sottofondo"; +"readium.a11y.conformance-a-compact" = "Questa pubblicazione soddisfa gli standard minimi di accessibilità"; +"readium.a11y.conformance-a-descriptive" = "La pubblicazione contiene una dichiarazione di conformità che attesta il rispetto degli standard EPUB Accessibility e WCAG 2 Livello A"; +"readium.a11y.conformance-aa-compact" = "Questa pubblicazione soddisfa gli standard di accessibilità accettati"; +"readium.a11y.conformance-aa-descriptive" = "La pubblicazione contiene una dichiarazione di conformità che attesta il rispetto degli standard EPUB Accessibility e WCAG 2 Livello AAA"; +"readium.a11y.conformance-aaa-compact" = "Questa pubblicazione supera gli standard di accessibilità"; +"readium.a11y.conformance-aaa-descriptive" = "La pubblicazione contiene una dichiarazione di conformità che attesta il rispetto degli standard EPUB Accessibility e WCAG 2 Livello AAA"; +"readium.a11y.conformance-certifier" = "La pubblicazione è stata certificata da "; +"readium.a11y.conformance-certifier-credentials" = "Le credenziali del certificatore sono "; +"readium.a11y.conformance-details-certification-info" = "La pubblicazione è stata certificata il "; +"readium.a11y.conformance-details-certifier-report" = "Per ulteriori informazioni, consultare il report di accessibilità del certificatore"; +"readium.a11y.conformance-details-claim" = "Questa pubblicazione è conforme ai requisiti di"; +"readium.a11y.conformance-details-epub-accessibility-1-0" = "EPUB Accessibility 1.0"; +"readium.a11y.conformance-details-epub-accessibility-1-1" = "EPUB Accessibility 1.1"; +"readium.a11y.conformance-details-level-a" = "Livello A"; +"readium.a11y.conformance-details-level-aa" = "Livello AA"; +"readium.a11y.conformance-details-level-aaa" = "Livello AAA"; +"readium.a11y.conformance-details-wcag-2-0-compact" = "WCAG 2.0"; +"readium.a11y.conformance-details-wcag-2-0-descriptive" = "Linee guida per l'accessibilità dei contenuti web (WCAG) 2.0"; +"readium.a11y.conformance-details-wcag-2-1-compact" = "WCAG 2.1"; +"readium.a11y.conformance-details-wcag-2-1-descriptive" = "Linee guida per l'accessibilità dei contenuti web (WCAG) 2.1"; +"readium.a11y.conformance-details-wcag-2-2-compact" = "WCAG 2.2"; +"readium.a11y.conformance-details-wcag-2-2-descriptive" = "Linee guida per l'accessibilità dei contenuti web (WCAG) 2.2"; +"readium.a11y.conformance-details-title" = "Informazioni dettagliate sulla conformità"; +"readium.a11y.conformance-no" = "Nessuna informazione disponibile"; +"readium.a11y.conformance-title" = "Conformità"; +"readium.a11y.conformance-unknown-standard" = "Nessuna indicazione sugli standard d'accessibilità"; +"readium.a11y.hazards-flashing-compact" = "Contenuto lampeggiante"; +"readium.a11y.hazards-flashing-descriptive" = "La pubblicazione contiene contenuti lampeggianti che possono causare crisi d'epilessia fotosensibile"; +"readium.a11y.hazards-flashing-none-compact" = "Nessun contenuto lampeggiante"; +"readium.a11y.hazards-flashing-none-descriptive" = "La pubblicazione non presenta contenuti lampeggianti che possono causare crisi d'epilessia fotosensibile"; +"readium.a11y.hazards-flashing-unknown-compact" = "Nessuna informazione sulla presenza di contenuti lampeggianti"; +"readium.a11y.hazards-flashing-unknown-descriptive" = "Non è stato possibile determinare la presenza di contenuti lampeggianti che possono causare crisi d'epilessia fotosensibile"; +"readium.a11y.hazards-motion-compact" = "Simulazione del movimento"; +"readium.a11y.hazards-motion-descriptive" = "La pubblicazione contiene simulazioni di movimento che possono provocare cinetosi"; +"readium.a11y.hazards-motion-none-compact" = "Nessun rischio di simulazione del movimento"; +"readium.a11y.hazards-motion-none-descriptive" = "La pubblicazione non contiene simulazioni di movimento che possono causare la malattia di movimento"; +"readium.a11y.hazards-motion-unknown-compact" = "Nessuna informazione relativa alla presenza di simulazioni di movimento"; +"readium.a11y.hazards-motion-unknown-descriptive" = "Non è stato possibile determinare la presenza di contenuti che possono provocare cinetosi"; +"readium.a11y.hazards-no-metadata" = "Nessuna informazione disponibile"; +"readium.a11y.hazards-none-compact" = "Nessuna problematica"; +"readium.a11y.hazards-none-descriptive" = "La pubblicazione non presenta contenuti a rischio di simulazione di movimento, di suoni, o di contenuti lampeggianti"; +"readium.a11y.hazards-sound-compact" = "Suoni"; +"readium.a11y.hazards-sound-descriptive" = "La pubblicazione contiene suoni che possono causare problemi di sensibilità"; +"readium.a11y.hazards-sound-none-compact" = "Nessun rischio acustico"; +"readium.a11y.hazards-sound-none-descriptive" = "La pubblicazione non contiene suoni che possono causare problemi di sensibilità"; +"readium.a11y.hazards-sound-unknown-compact" = "Nessuna informazione sulla presenza di suoni"; +"readium.a11y.hazards-sound-unknown-descriptive" = "Non è stato possibile determinare la presenza di suoni che potrebbero causare problemi di sensibilità"; +"readium.a11y.hazards-title" = "Problematiche"; +"readium.a11y.hazards-unknown" = "La presenza di rischi è sconosciuta"; +"readium.a11y.legal-considerations-exempt-compact" = "Dichiara di godere dell'esenzione d'accessibilità in alcune giurisdizioni"; +"readium.a11y.legal-considerations-exempt-descriptive" = "Questa risorsa gode dell'esenzione d'accessibilità in alcune giurisdizioni"; +"readium.a11y.legal-considerations-no-metadata" = "Nessuna informazione disponibile"; +"readium.a11y.legal-considerations-title" = "Note legali"; +"readium.a11y.navigation-index-compact" = "Indice analitico interattivo"; +"readium.a11y.navigation-index-descriptive" = "Indice analitico con link alle voci di riferimento"; +"readium.a11y.navigation-no-metadata" = "Nessuna informazione disponibile"; +"readium.a11y.navigation-page-navigation-compact" = "Vai alla pagina"; +"readium.a11y.navigation-page-navigation-descriptive" = "Sono presenti i riferimenti ai numeri di pagina della versione a stampa corrispondente"; +"readium.a11y.navigation-structural-compact" = "Intestazioni"; +"readium.a11y.navigation-structural-descriptive" = "Contiene elementi come titoli, elenchi e tabelle per permettere una navigazione strutturata"; +"readium.a11y.navigation-title" = "Navigazione"; +"readium.a11y.navigation-toc-compact" = "Indice interattivo"; +"readium.a11y.navigation-toc-descriptive" = "L’indice permette l’accesso diretto a tutti i capitoli tramite link"; +"readium.a11y.rich-content-accessible-chemistry-as-latex-compact" = "Formule chimiche in LaTeX"; +"readium.a11y.rich-content-accessible-chemistry-as-latex-descriptive" = "Formule chimiche in formato accessibile (LaTeX)"; +"readium.a11y.rich-content-accessible-chemistry-as-mathml-compact" = "Formule chimiche in MathML"; +"readium.a11y.rich-content-accessible-chemistry-as-mathml-descriptive" = "Formule chimiche in formato accessibile (MathML)"; +"readium.a11y.rich-content-accessible-math-as-latex-compact" = "Matematica in LaTeX"; +"readium.a11y.rich-content-accessible-math-as-latex-descriptive" = "Formule matematiche in formato accessibile (LaTeX)"; +"readium.a11y.rich-content-accessible-math-described" = "Sono disponibili descrizioni testuali per le formule matematiche"; +"readium.a11y.rich-content-closed-captions-compact" = "Sottotitoli disponibili per i video"; +"readium.a11y.rich-content-closed-captions-descriptive" = "Per i video sono disponibili dei sottotitoli"; +"readium.a11y.rich-content-extended-descriptions" = "Le immagini complesse presentano descrizioni estese"; +"readium.a11y.rich-content-math-as-mathml-compact" = "Matematica in MathML"; +"readium.a11y.rich-content-math-as-mathml-descriptive" = "Formule matematiche in formato accessibile (MathML)"; +"readium.a11y.rich-content-open-captions-compact" = "I video hanno i sottotitoli"; +"readium.a11y.rich-content-open-captions-descriptive" = "I video inclusi nella pubblicazione hanno i sottotitoli"; +"readium.a11y.rich-content-title" = "Contenuti arricchiti"; +"readium.a11y.rich-content-transcript" = "Trascrizioni fornite"; +"readium.a11y.rich-content-unknown" = "Nessuna informazione disponibile"; +"readium.a11y.ways-of-reading-nonvisual-reading-alt-text-compact" = "Immagini descritte"; +"readium.a11y.ways-of-reading-nonvisual-reading-alt-text-descriptive" = "Le immagini sono descritte da un testo"; +"readium.a11y.ways-of-reading-nonvisual-reading-no-metadata" = "Nessuna informazione sulla lettura non visiva"; +"readium.a11y.ways-of-reading-nonvisual-reading-none-compact" = "Non leggibile con lettura ad alta voce o in braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-none-descriptive" = "Il contenuto non è leggibile con la lettura ad alta voce o in braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-not-fully-compact" = "Non è interamente leggibile con lettura ad alta voce o in braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-not-fully-descriptive" = "Non tutti i contenuti potranno essere letti con lettura ad alta voce o in braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-readable-compact" = "Interamente leggibile con lettura ad alta voce o in braille"; +"readium.a11y.ways-of-reading-nonvisual-reading-readable-descriptive" = "Tutti i contenuti possono essere letti con la lettura ad alta voce o con il display braille"; +"readium.a11y.ways-of-reading-prerecorded-audio-complementary-compact" = "Clip audio preregistrate"; +"readium.a11y.ways-of-reading-prerecorded-audio-complementary-descriptive" = "Le clip audio preregistrate sono integrate nel contenuto"; +"readium.a11y.ways-of-reading-prerecorded-audio-no-metadata" = "Non sono disponibili informazioni sull'audio preregistrato"; +"readium.a11y.ways-of-reading-prerecorded-audio-only-compact" = "Solo audio preregistrato"; +"readium.a11y.ways-of-reading-prerecorded-audio-only-descriptive" = "Audiolibro senza testi alternativi"; +"readium.a11y.ways-of-reading-prerecorded-audio-synchronized-compact" = "Audio preregistrato sincronizzato con il testo"; +"readium.a11y.ways-of-reading-prerecorded-audio-synchronized-descriptive" = "Tutti i contenuti sono disponibili come audio preregistrato sincronizzato con il testo"; +"readium.a11y.ways-of-reading-title" = "Leggibilità"; +"readium.a11y.ways-of-reading-visual-adjustments-modifiable-compact" = "La formattazione del testo e il layout della pagina possono essere modificati"; +"readium.a11y.ways-of-reading-visual-adjustments-modifiable-descriptive" = "La formattazione del testo e il layout della pagina possono essere modificati in base alle funzionalità presenti nella soluzione di lettura (ingrandimento dei caratteri del testo, modifica dei colori e dei contrasti per il testo e lo sfondo, modifica degli spazi tra lettere, parole, frasi e paragrafi)"; +"readium.a11y.ways-of-reading-visual-adjustments-unknown" = "Non sono disponibili informazioni sulla possibilità di formattare il testo"; +"readium.a11y.ways-of-reading-visual-adjustments-unmodifiable-compact" = "La formattazione del testo e il display della pagina non possono essere modificati"; +"readium.a11y.ways-of-reading-visual-adjustments-unmodifiable-descriptive" = "Il layout di testo e pagina non può essere modificato poiché l'esperienza di lettura è vicina a una versione di stampa, ma i sistemi di lettura possono ancora fornire opzioni di zoom"; diff --git a/Support/Carthage/.xcodegen b/Support/Carthage/.xcodegen index 3c5e460df..534c32b8a 100644 --- a/Support/Carthage/.xcodegen +++ b/Support/Carthage/.xcodegen @@ -679,8 +679,12 @@ ../../Sources/Shared/Publication/Subject.swift ../../Sources/Shared/Publication/TDM.swift ../../Sources/Shared/Resources -../../Sources/Shared/Resources/en-US.lproj -../../Sources/Shared/Resources/en-US.lproj/W3CAccessibilityMetadataDisplayGuide.strings +../../Sources/Shared/Resources/en.lproj +../../Sources/Shared/Resources/en.lproj/W3CAccessibilityMetadataDisplayGuide.strings +../../Sources/Shared/Resources/fr.lproj +../../Sources/Shared/Resources/fr.lproj/W3CAccessibilityMetadataDisplayGuide.strings +../../Sources/Shared/Resources/it.lproj +../../Sources/Shared/Resources/it.lproj/W3CAccessibilityMetadataDisplayGuide.strings ../../Sources/Shared/Toolkit ../../Sources/Shared/Toolkit/Archive ../../Sources/Shared/Toolkit/Archive/ArchiveOpener.swift diff --git a/Support/Carthage/Readium.xcodeproj/project.pbxproj b/Support/Carthage/Readium.xcodeproj/project.pbxproj index 97a805b45..a19bcedc0 100644 --- a/Support/Carthage/Readium.xcodeproj/project.pbxproj +++ b/Support/Carthage/Readium.xcodeproj/project.pbxproj @@ -57,6 +57,7 @@ 1BF9469B4574D30E5C9BB75E /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF859D4933121BDC376CC8A /* Event.swift */; }; 1CB986C7E440F94F264A3567 /* EPUBSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4741AE26D76A8C2508437C2D /* EPUBSettings.swift */; }; 1D0B4067739311F6A54240E7 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4598F4671CE7BAE9299BF84B /* UIImage.swift */; }; + 1FB7DAE5EF125B0D05261318 /* W3CAccessibilityMetadataDisplayGuide.strings in Resources */ = {isa = PBXBuildFile; fileRef = A686B5257C30B0EA8087EB31 /* W3CAccessibilityMetadataDisplayGuide.strings */; }; 20D530EDB2B26ADECB4DAE82 /* EPUBDeobfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D785FEFDA202A61E620890 /* EPUBDeobfuscator.swift */; }; 216EA1C1ABA15836D60D910C /* GeneratedCoverService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925CDE3176715EBEBF40B21F /* GeneratedCoverService.swift */; }; 21B27CD89562506DDC1D62D1 /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A5959877EC9688CB0C370E /* Signature.swift */; }; @@ -206,7 +207,6 @@ 7E456E5AA21BCD712C325B62 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57074892837A37E3BFEDB481 /* String.swift */; }; 7E45E10720EA6B4F18196316 /* Metadata+Presentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC59A963F316359DF8B119AC /* Metadata+Presentation.swift */; }; 8029C2773AF704561B09BA99 /* DirectionalNavigationAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F7D914B293DF0A912613D2 /* DirectionalNavigationAdapter.swift */; }; - 8066A9FCBA3AA96717A01CFD /* W3CAccessibilityMetadataDisplayGuide.strings in Resources */ = {isa = PBXBuildFile; fileRef = 63AE10E3A29A24DD9C05C1D3 /* W3CAccessibilityMetadataDisplayGuide.strings */; }; 80FACAC721EBA4A11764482C /* EPUBPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5DA40519A11DDE69CDDBB1C /* EPUBPreferences.swift */; }; 81ADB258F083647221CED24F /* DataCompression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EBC685D4A0E07997088DD2D /* DataCompression.swift */; }; 824B5370C029445F0BE08741 /* Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8930DA1683F200ACE1AFC02A /* Format.swift */; }; @@ -616,6 +616,7 @@ 5124A0F95B52BA336E07C3D3 /* RelativeURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelativeURL.swift; sourceTree = ""; }; 529B55BE6996FCDC1082BF0A /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = ""; }; 5380F05215D8ED61B97F8021 /* PublicationOpener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicationOpener.swift; sourceTree = ""; }; + 538FDA65FCB39F10BF9C8BC0 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/W3CAccessibilityMetadataDisplayGuide.strings; sourceTree = ""; }; 53DAB92EBBB8031CA66B1E6F /* ReadiumAdapterLCPSQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReadiumAdapterLCPSQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5420CABB4B38006F64160F49 /* AccessibilityDisplayString+Generated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessibilityDisplayString+Generated.swift"; sourceTree = ""; }; 54699BC0E00F327E67908F6A /* Encryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encryption.swift; sourceTree = ""; }; @@ -636,7 +637,6 @@ 616C70674FBF020FE4607617 /* DeviceService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceService.swift; sourceTree = ""; }; 622CB8B75A568846FECA44D6 /* Streamable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Streamable.swift; sourceTree = ""; }; 626CFFF131E0E840B76428F1 /* DecorableNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecorableNavigator.swift; sourceTree = ""; }; - 63AE10E3A29A24DD9C05C1D3 /* W3CAccessibilityMetadataDisplayGuide.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = W3CAccessibilityMetadataDisplayGuide.strings; path = "en-US.lproj/W3CAccessibilityMetadataDisplayGuide.strings"; sourceTree = ""; }; 64ED7629E73022C1495081D1 /* Links.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Links.swift; sourceTree = ""; }; 6536C07F5A50F7F25FDBF69C /* ReadiumGCDWebServer.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ReadiumGCDWebServer.xcframework; path = ../../Carthage/Build/ReadiumGCDWebServer.xcframework; sourceTree = ""; }; 65C8719E9CC8EF0D2430AD85 /* CompletionList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionList.swift; sourceTree = ""; }; @@ -675,6 +675,7 @@ 7C28B8CD48F8A660141F5983 /* DefaultLocatorService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultLocatorService.swift; sourceTree = ""; }; 7C9B7B0A5A1B891BA3D9B9C0 /* BufferingResource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BufferingResource.swift; sourceTree = ""; }; 7CDE839ECF121D2EDD0C31C7 /* InputObservingGestureRecognizerAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputObservingGestureRecognizerAdapter.swift; sourceTree = ""; }; + 7E14E1BA1A6B15BBC1C19296 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/W3CAccessibilityMetadataDisplayGuide.strings; sourceTree = ""; }; 7EE333717736247C6F846CEF /* AudioPublicationManifestAugmentor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPublicationManifestAugmentor.swift; sourceTree = ""; }; 7FCA24A94D6376487FECAEF1 /* LCPPassphraseRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LCPPassphraseRepository.swift; sourceTree = ""; }; 819D931708B3EE95CF9ADFED /* OPDSCopies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPDSCopies.swift; sourceTree = ""; }; @@ -849,6 +850,7 @@ F07214E263C6589987A561F9 /* SQLite.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = SQLite.xcframework; path = ../../Carthage/Build/SQLite.xcframework; sourceTree = ""; }; F1F5FEE0323287B9CAA09F03 /* MediaOverlays.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaOverlays.swift; sourceTree = ""; }; F2E780027410F4B6CC872B3D /* OPDSAvailability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPDSAvailability.swift; sourceTree = ""; }; + F46CAAA92BFBFCCC24AD324A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/W3CAccessibilityMetadataDisplayGuide.strings; sourceTree = ""; }; F4FC8F971F00B5876803B62A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; F5C6D0C5860E802EDA23068C /* EditingAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditingAction.swift; sourceTree = ""; }; F5DA40519A11DDE69CDDBB1C /* EPUBPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPUBPreferences.swift; sourceTree = ""; }; @@ -1482,7 +1484,7 @@ 699E0FDF48F79D5EEACE0436 /* Resources */ = { isa = PBXGroup; children = ( - 63AE10E3A29A24DD9C05C1D3 /* W3CAccessibilityMetadataDisplayGuide.strings */, + A686B5257C30B0EA8087EB31 /* W3CAccessibilityMetadataDisplayGuide.strings */, ); path = Resources; sourceTree = ""; @@ -2304,8 +2306,8 @@ knownRegions = ( Base, en, - "en-US", fr, + it, ); mainGroup = 2C63ECC3CC1230CCA416F55F; minimizedProjectReferenceProxies = 1; @@ -2356,7 +2358,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8066A9FCBA3AA96717A01CFD /* W3CAccessibilityMetadataDisplayGuide.strings in Resources */, + 1FB7DAE5EF125B0D05261318 /* W3CAccessibilityMetadataDisplayGuide.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2862,6 +2864,16 @@ name = Localizable.strings; sourceTree = ""; }; + A686B5257C30B0EA8087EB31 /* W3CAccessibilityMetadataDisplayGuide.strings */ = { + isa = PBXVariantGroup; + children = ( + 7E14E1BA1A6B15BBC1C19296 /* en */, + F46CAAA92BFBFCCC24AD324A /* fr */, + 538FDA65FCB39F10BF9C8BC0 /* it */, + ); + name = W3CAccessibilityMetadataDisplayGuide.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/Tests/SharedTests/Publication/Accessibility/AccessibilityMetadataDisplayGuideTests.swift b/Tests/SharedTests/Publication/Accessibility/AccessibilityMetadataDisplayGuideTests.swift index e6d72e1fa..3720fa74f 100644 --- a/Tests/SharedTests/Publication/Accessibility/AccessibilityMetadataDisplayGuideTests.swift +++ b/Tests/SharedTests/Publication/Accessibility/AccessibilityMetadataDisplayGuideTests.swift @@ -30,6 +30,32 @@ class AccessibilityMetadataDisplayGuideTests: XCTestCase { ) } + /// Tests the fallback behavior for strings without -compact/-descriptive + /// suffixes. + /// Some strings in thorium-locales have identical compact and descriptive + /// values, so they are stored with only the base key. + func testDisplayStatementLocalizedStringFallbackToBaseKey() { + // Test hazardsNoMetadata + XCTAssertEqual( + AccessibilityDisplayString.hazardsNoMetadata.localized(descriptive: false).string, + "No information is available" + ) + XCTAssertEqual( + AccessibilityDisplayString.hazardsNoMetadata.localized(descriptive: true).string, + "No information is available" + ) + + // Test conformanceNo + XCTAssertEqual( + AccessibilityDisplayString.additionalAccessibilityInformationRubyAnnotations.localized(descriptive: false).string, + "Some Ruby annotations" + ) + XCTAssertEqual( + AccessibilityDisplayString.additionalAccessibilityInformationRubyAnnotations.localized(descriptive: true).string, + "Some Ruby annotations" + ) + } + func testDisplayStatementCustomLocalizedString() { let statement = AccessibilityDisplayStatement( string: .waysOfReadingNonvisualReadingReadable, @@ -433,9 +459,9 @@ class AccessibilityMetadataDisplayGuideTests: XCTestCase { transcript: true ).statements.map(\.id), [ - .richContentExtended, + .richContentExtendedDescriptions, .richContentAccessibleMathDescribed, - .richContentAccessibleMathAsMathml, + .richContentMathAsMathml, .richContentAccessibleMathAsLatex, .richContentAccessibleChemistryAsMathml, .richContentAccessibleChemistryAsLatex, @@ -459,7 +485,7 @@ class AccessibilityMetadataDisplayGuideTests: XCTestCase { transcript: false ).statements.map(\.id), [ - .richContentExtended, + .richContentExtendedDescriptions, ] ) @@ -493,7 +519,7 @@ class AccessibilityMetadataDisplayGuideTests: XCTestCase { transcript: false ).statements.map(\.id), [ - .richContentAccessibleMathAsMathml, + .richContentMathAsMathml, ] )