diff --git a/bin/locale-packs.js b/bin/locale-packs.js deleted file mode 100644 index ff58b27c76..0000000000 --- a/bin/locale-packs.js +++ /dev/null @@ -1,295 +0,0 @@ -const glob = require('glob') -const { ESLint } = require('eslint') -const chalk = require('chalk') -const path = require('path') -const dedent = require('dedent') -const stringifyObject = require('stringify-object') -const fs = require('fs') -const Uppy = require('../packages/@uppy/core') - -const uppy = new Uppy() - -function getSources (pluginName) { - const dependencies = { - // because 'provider-views' doesn't have its own locale, it uses Core's defaultLocale - core: ['provider-views'], - } - - const globPath = path.join(__dirname, '..', 'packages', '@uppy', pluginName, 'lib', '**', '*.js') - let contents = glob.sync(globPath).map((file) => { - return fs.readFileSync(file, 'utf-8') - }) - - if (dependencies[pluginName]) { - dependencies[pluginName].forEach((addPlugin) => { - contents = contents.concat(getSources(addPlugin)) - }) - } - - return contents -} - -function buildPluginsList () { - const plugins = {} - const sources = {} - - // Go over all uppy plugins, check if they are constructors - // and instanciate them, check for defaultLocale property, - // then add to plugins object - - const packagesGlobPath = path.join(__dirname, '..', 'packages', '@uppy', '*', 'package.json') - const files = glob.sync(packagesGlobPath) - - console.log('--> Checked plugins could be instantiated and have defaultLocale in them:\n') - for (const file of files) { - const dirName = path.dirname(file) - const pluginName = path.basename(dirName) - if (pluginName === 'locales' - || pluginName === 'react-native' - || pluginName === 'vue' - || pluginName === 'svelte' - || pluginName === 'angular') { - continue // eslint-disable-line no-continue - } - const Plugin = require(dirName) // eslint-disable-line global-require, import/no-dynamic-require - let plugin - - // A few hacks to emulate browser environment because e.g.: - // GoldenRetrieves calls upon MetaDataStore in the constructor, which uses localStorage - // @TODO Consider rewriting constructors so they don't make imperative calls that rely on browser environment - // (OR: just keep this browser mocking, if it's only causing issues for this script, it doesn't matter) - global.location = { protocol: 'https' } - global.navigator = { userAgent: '' } - global.localStorage = { - key: () => { }, - getItem: () => { }, - } - global.window = { - indexedDB: { - open: () => { return {} }, - }, - } - global.document = { - createElement: () => { - return { style: {} } - }, - get body () { return this.createElement() }, - } - - try { - if (pluginName === 'provider-views') { - plugin = new Plugin(plugins['drag-drop'], { - companionPattern: '', - companionUrl: 'https://companion.uppy.io', - }) - } else if (pluginName === 'store-redux') { - plugin = new Plugin({ store: { dispatch: () => { } } }) - } else { - plugin = new Plugin(uppy, { - companionPattern: '', - companionUrl: 'https://companion.uppy.io', - params: { - auth: { - key: 'x', - }, - }, - }) - } - } catch (err) { - if (err.message !== 'Plugin is not a constructor') { - console.error(`--> While trying to instantiate plugin: ${pluginName}, this error was thrown: `) - throw err - } - } - - if (plugin && plugin.defaultLocale) { - console.log(`[x] Check plugin: ${pluginName}`) - plugins[pluginName] = plugin - sources[pluginName] = getSources(pluginName) - } else { - console.log(`[ ] Check plugin: ${pluginName}`) - } - } - - console.log('') - - return { plugins, sources } -} - -function addLocaleToPack (localePack, plugin, pluginName) { - const localeStrings = plugin.defaultLocale.strings - - for (const key of Object.keys(localeStrings)) { - const valueInPlugin = JSON.stringify(localeStrings[key]) - const valueInPack = JSON.stringify(localePack[key]) - - if (key in localePack && valueInPlugin !== valueInPack) { - console.error(`⚠ Plugin ${chalk.magenta(pluginName)} has a duplicate key: ${chalk.magenta(key)}`) - console.error(` Value in plugin: ${chalk.cyan(valueInPlugin)}`) - console.error(` Value in pack : ${chalk.yellow(valueInPack)}`) - console.error() - throw new Error(`Duplicate locale key: '${key}'`) - } - localePack[key] = localeStrings[key] // eslint-disable-line no-param-reassign - } -} - -function checkForUnused (fileContents, pluginName, localePack) { - const buff = fileContents.join('\n') - for (const key of Object.keys(localePack)) { - const regPat = new RegExp(`(i18n|i18nArray)\\([^\\)]*['\`"]${key}['\`"]`, 'g') - if (!buff.match(regPat)) { - console.error(`⚠ defaultLocale key: ${chalk.magenta(key)} not used in plugin: ${chalk.cyan(pluginName)}`) - throw new Error(`Unused locale key: '${key}'`) - } - } -} - -function sortObjectAlphabetically (obj) { - return Object.fromEntries(Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))) -} - -function createTypeScriptLocale (plugin, pluginName) { - const allowedStringTypes = Object.keys(plugin.defaultLocale.strings) - .map(key => ` | '${key}'`) - .join('\n') - - const pluginClassName = pluginName === 'core' ? 'Core' : plugin.id - const localePath = path.join(__dirname, '..', 'packages', '@uppy', pluginName, 'types', 'generatedLocale.d.ts') - - const localeTypes = dedent` - /* eslint-disable */ - import type { Locale } from '@uppy/core' - - type ${pluginClassName}Locale = Locale< - ${allowedStringTypes} - > - - export default ${pluginClassName}Locale - ` - - fs.writeFileSync(localePath, localeTypes) -} - -async function build () { - let localePack = {} - const { plugins, sources } = buildPluginsList() - - for (const [pluginName, plugin] of Object.entries(plugins)) { - addLocaleToPack(localePack, plugin, pluginName) - } - - for (const [pluginName, plugin] of Object.entries(plugins)) { - createTypeScriptLocale(plugin, pluginName) - } - - localePack = sortObjectAlphabetically(localePack) - - for (const [pluginName, source] of Object.entries(sources)) { - checkForUnused(source, pluginName, sortObjectAlphabetically(plugins[pluginName].defaultLocale.strings)) - } - - const prettyLocale = stringifyObject(localePack, { - indent: ' ', - singleQuotes: true, - inlineCharacterLimit: 12, - }) - - const localeTemplatePath = path.join(__dirname, '..', 'packages', '@uppy', 'locales', 'template.js') - const template = fs.readFileSync(localeTemplatePath, 'utf-8') - - const finalLocale = template.replace('en_US.strings = {}', `en_US.strings = ${prettyLocale}`) - - const localePackagePath = path.join(__dirname, '..', 'packages', '@uppy', 'locales', 'src', 'en_US.js') - - const linter = new ESLint({ - fix: true, - }) - - const [lintResult] = await linter.lintText(finalLocale, { - filePath: localePackagePath, - }) - fs.writeFileSync(localePackagePath, lintResult.output, 'utf8') - - console.log(`✅ Written '${localePackagePath}'`) -} - -function test () { - const leadingLocaleName = 'en_US' - - const followerLocales = {} - const followerValues = {} - const localePackagePath = path.join(__dirname, '..', 'packages', '@uppy', 'locales', 'src', '*.js') - glob.sync(localePackagePath).forEach((localePath) => { - const localeName = path.basename(localePath, '.js') - - // Builds array with items like: 'uploadingXFiles' - // We do not check nested items because different languages may have different amounts of plural forms. - // eslint-disable-next-line global-require, import/no-dynamic-require - followerValues[localeName] = require(localePath).strings - followerLocales[localeName] = Object.keys(followerValues[localeName]) - }) - - // Take aside our leading locale: en_US - const leadingLocale = followerLocales[leadingLocaleName] - const leadingValues = followerValues[leadingLocaleName] - delete followerLocales[leadingLocaleName] - - // Compare all follower Locales (RU, DE, etc) with our leader en_US - const warnings = [] - const fatals = [] - for (const [followerName, followerLocale] of Object.entries(followerLocales)) { - const missing = leadingLocale.filter((key) => !followerLocale.includes(key)) - const excess = followerLocale.filter((key) => !leadingLocale.includes(key)) - - missing.forEach((key) => { - // Items missing are a non-fatal warning because we don't want CI to bum out over all languages - // as soon as we add some English - let value = leadingValues[key] - if (typeof value === 'object') { - // For values with plural forms, just take the first one right now - value = value[Object.keys(value)[0]] - } - warnings.push(`${chalk.cyan(followerName)} locale has missing string: '${chalk.red(key)}' that is present in ${chalk.cyan(leadingLocaleName)} with value: ${chalk.yellow(leadingValues[key])}`) - }) - excess.forEach((key) => { - // Items in excess are a fatal because we should clean up follower languages once we remove English strings - fatals.push(`${chalk.cyan(followerName)} locale has excess string: '${chalk.yellow(key)}' that is not present in ${chalk.cyan(leadingLocaleName)}. `) - }) - } - - if (warnings.length) { - console.error('--> Locale warnings: ') - console.error(warnings.join('\n')) - console.error('') - } - if (fatals.length) { - console.error('--> Locale fatal warnings: ') - console.error(fatals.join('\n')) - console.error('') - process.exit(1) - } - - if (!warnings.length && !fatals.length) { - console.log(`--> All locale strings have matching keys ${chalk.green(': )')}`) - console.log('') - } -} - -async function main () { - console.warn('\n--> Make sure to run `npm run build:lib` for this locale script to work properly. ') - - const mode = process.argv[2] - if (mode === 'build') { - await build() - } else if (mode === 'test') { - test() - } else { - throw new Error("First argument must be either 'build' or 'test'") - } -} - -main().catch((err) => { - console.error(err) - process.exit(1) -}) diff --git a/package.json b/package.json index 24f41f738c..54eed56092 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "build:angular": "yarn workspace @uppy/angular build:release", "build:js": "npm-run-all build:lib build:companion build:locale-pack build:svelte build:angular build:bundle", "build:lib": "yarn node ./bin/build-lib.js", - "build:locale-pack": "yarn node --experimental-abortcontroller ./bin/locale-packs.js build", + "build:locale-pack": "yarn workspace locale-pack build && eslint packages/@uppy/locales/src/en_US.js --fix && yarn workspace locale-pack test unused", "build": "npm-run-all --parallel build:js build:css --serial size", "contributors:save": "yarn node ./bin/update-contributors.mjs", "dev:browsersync": "yarn workspace @uppy-example/dev start", @@ -156,7 +156,8 @@ "test:companion": "yarn workspace @uppy/companion test", "test:endtoend:local": "yarn workspace @uppy-tests/end2end test:endtoend:local", "test:endtoend": "yarn workspace @uppy-tests/end2end test:endtoend", - "test:locale-packs": "yarn node ./bin/locale-packs.js test", + "test:locale-packs:unused": "yarn workspace locale-pack test unused", + "test:locale-packs:warnings": "yarn workspace locale-pack test warnings", "test:type": "yarn workspaces foreach -piv --include '@uppy/*' --exclude '@uppy/{angular,react-native,locales,companion,provider-views,robodog,svelte}' exec tsd", "test:unit": "yarn run build:lib && jest --env jsdom", "test:watch": "jest --env jsdom --watch", diff --git a/packages/@uppy/aws-s3/src/index.js b/packages/@uppy/aws-s3/src/index.js index b2949df241..512aa9722d 100644 --- a/packages/@uppy/aws-s3/src/index.js +++ b/packages/@uppy/aws-s3/src/index.js @@ -31,6 +31,8 @@ const { RequestClient } = require('@uppy/companion-client') const MiniXHRUpload = require('./MiniXHRUpload') const isXml = require('./isXml') +const locale = require('./locale') + function resolveUrl (origin, link) { return new URL(link, origin || undefined).toString() } @@ -108,11 +110,7 @@ module.exports = class AwsS3 extends BasePlugin { this.id = this.opts.id || 'AwsS3' this.title = 'AWS S3' - this.defaultLocale = { - strings: { - timedOut: 'Upload stalled for %{seconds} seconds, aborting.', - }, - } + this.defaultLocale = locale const defaultOptions = { timeout: 30 * 1000, diff --git a/packages/@uppy/aws-s3/src/locale.js b/packages/@uppy/aws-s3/src/locale.js new file mode 100644 index 0000000000..b043ff91c2 --- /dev/null +++ b/packages/@uppy/aws-s3/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + timedOut: 'Upload stalled for %{seconds} seconds, aborting.', + }, +} diff --git a/packages/@uppy/box/src/index.js b/packages/@uppy/box/src/index.js index b0a6a4a7e3..064b0fa3f8 100644 --- a/packages/@uppy/box/src/index.js +++ b/packages/@uppy/box/src/index.js @@ -3,6 +3,8 @@ const { Provider } = require('@uppy/companion-client') const { ProviderViews } = require('@uppy/provider-views') const { h } = require('preact') +const locale = require('./locale') + module.exports = class Box extends UIPlugin { static VERSION = require('../package.json').version @@ -32,11 +34,8 @@ module.exports = class Box extends UIPlugin { pluginId: this.id, }) - this.defaultLocale = { - strings: { - pluginNameBox: 'Box', - }, - } + this.defaultLocale = locale + this.i18nInit() this.title = this.i18n('pluginNameBox') diff --git a/packages/@uppy/box/src/locale.js b/packages/@uppy/box/src/locale.js new file mode 100644 index 0000000000..18e820dac2 --- /dev/null +++ b/packages/@uppy/box/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + pluginNameBox: 'Box', + }, +} diff --git a/packages/@uppy/core/src/Uppy.js b/packages/@uppy/core/src/Uppy.js index 8a39ad27dc..100365f0c4 100644 --- a/packages/@uppy/core/src/Uppy.js +++ b/packages/@uppy/core/src/Uppy.js @@ -16,6 +16,8 @@ const supportsUploadProgress = require('./supportsUploadProgress') const getFileName = require('./getFileName') const { justErrorsLogger, debugLogger } = require('./loggers') +const locale = require('./locale') + // Exported from here. class RestrictionError extends Error { constructor (...args) { @@ -68,60 +70,7 @@ class Uppy { * @param {object} opts — Uppy options */ constructor (opts) { - this.defaultLocale = { - strings: { - addBulkFilesFailed: { - 0: 'Failed to add %{smart_count} file due to an internal error', - 1: 'Failed to add %{smart_count} files due to internal errors', - }, - youCanOnlyUploadX: { - 0: 'You can only upload %{smart_count} file', - 1: 'You can only upload %{smart_count} files', - }, - youHaveToAtLeastSelectX: { - 0: 'You have to select at least %{smart_count} file', - 1: 'You have to select at least %{smart_count} files', - }, - exceedsSize: '%{file} exceeds maximum allowed size of %{size}', - missingRequiredMetaField: 'Missing required meta fields', - missingRequiredMetaFieldOnFile: 'Missing required meta fields in %{fileName}', - inferiorSize: 'This file is smaller than the allowed size of %{size}', - youCanOnlyUploadFileTypes: 'You can only upload: %{types}', - noMoreFilesAllowed: 'Cannot add more files', - noDuplicates: 'Cannot add the duplicate file \'%{fileName}\', it already exists', - companionError: 'Connection with Companion failed', - authAborted: 'Authentication aborted', - companionUnauthorizeHint: 'To unauthorize to your %{provider} account, please go to %{url}', - failedToUpload: 'Failed to upload %{file}', - noInternetConnection: 'No Internet connection', - connectedToInternet: 'Connected to the Internet', - // Strings for remote providers - noFilesFound: 'You have no files or folders here', - selectX: { - 0: 'Select %{smart_count}', - 1: 'Select %{smart_count}', - }, - allFilesFromFolderNamed: 'All files from folder %{name}', - openFolderNamed: 'Open folder %{name}', - cancel: 'Cancel', - logOut: 'Log out', - filter: 'Filter', - resetFilter: 'Reset filter', - loading: 'Loading...', - authenticateWithTitle: 'Please authenticate with %{pluginName} to select files', - authenticateWith: 'Connect to %{pluginName}', - signInWithGoogle: 'Sign in with Google', - searchImages: 'Search for images', - enterTextToSearch: 'Enter text to search for images', - backToSearch: 'Back to Search', - emptyFolderAdded: 'No files were added from empty folder', - folderAlreadyAdded: 'The folder "%{folder}" was already added', - folderAdded: { - 0: 'Added %{smart_count} file from %{folder}', - 1: 'Added %{smart_count} files from %{folder}', - }, - }, - } + this.defaultLocale = locale const defaultOptions = { id: 'uppy', diff --git a/packages/@uppy/core/src/locale.js b/packages/@uppy/core/src/locale.js new file mode 100644 index 0000000000..3423c9366a --- /dev/null +++ b/packages/@uppy/core/src/locale.js @@ -0,0 +1,58 @@ +module.exports = { + strings: { + addBulkFilesFailed: { + 0: 'Failed to add %{smart_count} file due to an internal error', + 1: 'Failed to add %{smart_count} files due to internal errors', + }, + youCanOnlyUploadX: { + 0: 'You can only upload %{smart_count} file', + 1: 'You can only upload %{smart_count} files', + }, + youHaveToAtLeastSelectX: { + 0: 'You have to select at least %{smart_count} file', + 1: 'You have to select at least %{smart_count} files', + }, + exceedsSize: '%{file} exceeds maximum allowed size of %{size}', + missingRequiredMetaField: 'Missing required meta fields', + missingRequiredMetaFieldOnFile: + 'Missing required meta fields in %{fileName}', + inferiorSize: 'This file is smaller than the allowed size of %{size}', + youCanOnlyUploadFileTypes: 'You can only upload: %{types}', + noMoreFilesAllowed: 'Cannot add more files', + noDuplicates: + "Cannot add the duplicate file '%{fileName}', it already exists", + companionError: 'Connection with Companion failed', + authAborted: 'Authentication aborted', + companionUnauthorizeHint: + 'To unauthorize to your %{provider} account, please go to %{url}', + failedToUpload: 'Failed to upload %{file}', + noInternetConnection: 'No Internet connection', + connectedToInternet: 'Connected to the Internet', + // Strings for remote providers + noFilesFound: 'You have no files or folders here', + selectX: { + 0: 'Select %{smart_count}', + 1: 'Select %{smart_count}', + }, + allFilesFromFolderNamed: 'All files from folder %{name}', + openFolderNamed: 'Open folder %{name}', + cancel: 'Cancel', + logOut: 'Log out', + filter: 'Filter', + resetFilter: 'Reset filter', + loading: 'Loading...', + authenticateWithTitle: + 'Please authenticate with %{pluginName} to select files', + authenticateWith: 'Connect to %{pluginName}', + signInWithGoogle: 'Sign in with Google', + searchImages: 'Search for images', + enterTextToSearch: 'Enter text to search for images', + backToSearch: 'Back to Search', + emptyFolderAdded: 'No files were added from empty folder', + folderAlreadyAdded: 'The folder "%{folder}" was already added', + folderAdded: { + 0: 'Added %{smart_count} file from %{folder}', + 1: 'Added %{smart_count} files from %{folder}', + }, + }, +} diff --git a/packages/@uppy/dashboard/src/index.js b/packages/@uppy/dashboard/src/index.js index da1e7988db..f40dcb39d0 100644 --- a/packages/@uppy/dashboard/src/index.js +++ b/packages/@uppy/dashboard/src/index.js @@ -14,6 +14,8 @@ const memoize = require('memoize-one').default || require('memoize-one') const FOCUSABLE_ELEMENTS = require('@uppy/utils/lib/FOCUSABLE_ELEMENTS') const DashboardUI = require('./components/Dashboard') +const locale = require('./locale') + const TAB_KEY = 9 const ESC_KEY = 27 @@ -47,70 +49,7 @@ module.exports = class Dashboard extends UIPlugin { this.type = 'orchestrator' this.modalName = `uppy-Dashboard-${nanoid()}` - this.defaultLocale = { - strings: { - closeModal: 'Close Modal', - importFrom: 'Import from %{name}', - addingMoreFiles: 'Adding more files', - addMoreFiles: 'Add more files', - dashboardWindowTitle: 'File Uploader Window (Press escape to close)', - dashboardTitle: 'File Uploader', - copyLinkToClipboardSuccess: 'Link copied to clipboard', - copyLinkToClipboardFallback: 'Copy the URL below', - copyLink: 'Copy link', - back: 'Back', - addMore: 'Add more', - removeFile: 'Remove file %{file}', - editFile: 'Edit file', - editFileWithFilename: 'Edit file %{file}', - editing: 'Editing %{file}', - finishEditingFile: 'Finish editing file', - save: 'Save', - saveChanges: 'Save changes', - cancel: 'Cancel', - myDevice: 'My Device', - dropPasteFiles: 'Drop files here or %{browseFiles}', - dropPasteFolders: 'Drop files here or %{browseFolders}', - dropPasteBoth: 'Drop files here, %{browseFiles} or %{browseFolders}', - dropPasteImportFiles: 'Drop files here, %{browseFiles} or import from:', - dropPasteImportFolders: 'Drop files here, %{browseFolders} or import from:', - dropPasteImportBoth: 'Drop files here, %{browseFiles}, %{browseFolders} or import from:', - importFiles: 'Import files from:', - dropHint: 'Drop your files here', - browseFiles: 'browse files', - browseFolders: 'browse folders', - uploadComplete: 'Upload complete', - uploadPaused: 'Upload paused', - resumeUpload: 'Resume upload', - pauseUpload: 'Pause upload', - retryUpload: 'Retry upload', - cancelUpload: 'Cancel upload', - xFilesSelected: { - 0: '%{smart_count} file selected', - 1: '%{smart_count} files selected', - }, - uploadingXFiles: { - 0: 'Uploading %{smart_count} file', - 1: 'Uploading %{smart_count} files', - }, - processingXFiles: { - 0: 'Processing %{smart_count} file', - 1: 'Processing %{smart_count} files', - }, - recoveredXFiles: { - 0: 'We could not fully recover 1 file. Please re-select it and resume the upload.', - 1: 'We could not fully recover %{smart_count} files. Please re-select them and resume the upload.', - }, - recoveredAllFiles: 'We restored all files. You can now resume the upload.', - sessionRestored: 'Session restored', - reSelect: 'Re-select', - poweredBy: 'Powered by %{uppy}', - missingRequiredMetaFields: { - 0: 'Missing required meta field: %{fields}.', - 1: 'Missing required meta fields: %{fields}.', - }, - }, - } + this.defaultLocale = locale // set default options const defaultOptions = { diff --git a/packages/@uppy/dashboard/src/locale.js b/packages/@uppy/dashboard/src/locale.js new file mode 100644 index 0000000000..8030311d36 --- /dev/null +++ b/packages/@uppy/dashboard/src/locale.js @@ -0,0 +1,88 @@ +module.exports = { + strings: { + // When `inline: false`, used as the screen reader label for the button that closes the modal. + closeModal: 'Close Modal', + // Used as the screen reader label for the plus (+) button that shows the “Add more files” screen + addMoreFiles: 'Add more files', + addingMoreFiles: 'Adding more files', + // Used as the header for import panels, e.g., “Import from Google Drive”. + importFrom: 'Import from %{name}', + // When `inline: false`, used as the screen reader label for the dashboard modal. + dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)', + // When `inline: true`, used as the screen reader label for the dashboard area. + dashboardTitle: 'Uppy Dashboard', + // Shown in the Informer when a link to a file was copied to the clipboard. + copyLinkToClipboardSuccess: 'Link copied to clipboard.', + // Used when a link cannot be copied automatically — the user has to select the text from the + // input element below this string. + copyLinkToClipboardFallback: 'Copy the URL below', + // Used as the hover title and screen reader label for buttons that copy a file link. + copyLink: 'Copy link', + back: 'Back', + // Used as the screen reader label for buttons that remove a file. + removeFile: 'Remove file', + // Used as the screen reader label for buttons that open the metadata editor panel for a file. + editFile: 'Edit file', + // Shown in the panel header for the metadata editor. Rendered as “Editing image.png”. + editing: 'Editing %{file}', + // Used as the screen reader label for the button that saves metadata edits and returns to the + // file list view. + finishEditingFile: 'Finish editing file', + saveChanges: 'Save changes', + // Used as the label for the tab button that opens the system file selection dialog. + myDevice: 'My Device', + dropHint: 'Drop your files here', + // Used as the hover text and screen reader label for file progress indicators when + // they have been fully uploaded. + uploadComplete: 'Upload complete', + uploadPaused: 'Upload paused', + // Used as the hover text and screen reader label for the buttons to resume paused uploads. + resumeUpload: 'Resume upload', + // Used as the hover text and screen reader label for the buttons to pause uploads. + pauseUpload: 'Pause upload', + // Used as the hover text and screen reader label for the buttons to retry failed uploads. + retryUpload: 'Retry upload', + // Used as the hover text and screen reader label for the buttons to cancel uploads. + cancelUpload: 'Cancel upload', + // Used in a title, how many files are currently selected + xFilesSelected: { + 0: '%{smart_count} file selected', + 1: '%{smart_count} files selected', + }, + uploadingXFiles: { + 0: 'Uploading %{smart_count} file', + 1: 'Uploading %{smart_count} files', + }, + processingXFiles: { + 0: 'Processing %{smart_count} file', + 1: 'Processing %{smart_count} files', + }, + // The "powered by Uppy" link at the bottom of the Dashboard. + poweredBy: 'Powered by %{uppy}', + addMore: 'Add more', + editFileWithFilename: 'Edit file %{file}', + save: 'Save', + cancel: 'Cancel', + dropPasteFiles: 'Drop files here or %{browseFiles}', + dropPasteFolders: 'Drop files here or %{browseFolders}', + dropPasteBoth: 'Drop files here, %{browseFiles} or %{browseFolders}', + dropPasteImportFiles: 'Drop files here, %{browseFiles} or import from:', + dropPasteImportFolders: 'Drop files here, %{browseFolders} or import from:', + dropPasteImportBoth: + 'Drop files here, %{browseFiles}, %{browseFolders} or import from:', + importFiles: 'Import files from:', + browseFiles: 'browse files', + browseFolders: 'browse folders', + recoveredXFiles: { + 0: 'We could not fully recover 1 file. Please re-select it and resume the upload.', + 1: 'We could not fully recover %{smart_count} files. Please re-select them and resume the upload.', + }, + recoveredAllFiles: 'We restored all files. You can now resume the upload.', + sessionRestored: 'Session restored', + reSelect: 'Re-select', + missingRequiredMetaFields: { + 0: 'Missing required meta field: %{fields}.', + 1: 'Missing required meta fields: %{fields}.', + }, + }, +} diff --git a/packages/@uppy/drag-drop/src/index.js b/packages/@uppy/drag-drop/src/index.js index 835ce16708..e50e009ad0 100644 --- a/packages/@uppy/drag-drop/src/index.js +++ b/packages/@uppy/drag-drop/src/index.js @@ -4,6 +4,8 @@ const isDragDropSupported = require('@uppy/utils/lib/isDragDropSupported') const getDroppedFiles = require('@uppy/utils/lib/getDroppedFiles') const { h } = require('preact') +const locale = require('./locale.js') + /** * Drag & Drop plugin * @@ -18,12 +20,7 @@ module.exports = class DragDrop extends UIPlugin { this.id = this.opts.id || 'DragDrop' this.title = 'Drag & Drop' - this.defaultLocale = { - strings: { - dropHereOr: 'Drop files here or %{browse}', - browse: 'browse', - }, - } + this.defaultLocale = locale // Default options const defaultOpts = { diff --git a/packages/@uppy/drag-drop/src/locale.js b/packages/@uppy/drag-drop/src/locale.js new file mode 100644 index 0000000000..d5c0446d93 --- /dev/null +++ b/packages/@uppy/drag-drop/src/locale.js @@ -0,0 +1,9 @@ +module.exports = { + strings: { + // Text to show on the droppable area. + // `%{browse}` is replaced with a link that opens the system file selection dialog. + dropHereOr: 'Drop here or %{browse}', + // Used as the label for the link that opens the system file selection dialog. + browse: 'browse', + }, +} diff --git a/packages/@uppy/dropbox/src/index.js b/packages/@uppy/dropbox/src/index.js index ed0b9dd8eb..a10187b7a6 100644 --- a/packages/@uppy/dropbox/src/index.js +++ b/packages/@uppy/dropbox/src/index.js @@ -3,6 +3,8 @@ const { Provider } = require('@uppy/companion-client') const { ProviderViews } = require('@uppy/provider-views') const { h } = require('preact') +const locale = require('./locale') + module.exports = class Dropbox extends UIPlugin { static VERSION = require('../package.json').version @@ -29,11 +31,8 @@ module.exports = class Dropbox extends UIPlugin { pluginId: this.id, }) - this.defaultLocale = { - strings: { - pluginNameDropbox: 'Dropbox', - }, - } + this.defaultLocale = locale + this.i18nInit() this.title = this.i18n('pluginNameDropbox') diff --git a/packages/@uppy/dropbox/src/locale.js b/packages/@uppy/dropbox/src/locale.js new file mode 100644 index 0000000000..2c86a87245 --- /dev/null +++ b/packages/@uppy/dropbox/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + pluginNameDropbox: 'Dropbox', + }, +} diff --git a/packages/@uppy/facebook/src/index.js b/packages/@uppy/facebook/src/index.js index 40e11be4da..fe1390ac12 100644 --- a/packages/@uppy/facebook/src/index.js +++ b/packages/@uppy/facebook/src/index.js @@ -3,6 +3,8 @@ const { Provider } = require('@uppy/companion-client') const { ProviderViews } = require('@uppy/provider-views') const { h } = require('preact') +const locale = require('./locale.js') + module.exports = class Facebook extends UIPlugin { static VERSION = require('../package.json').version @@ -29,11 +31,8 @@ module.exports = class Facebook extends UIPlugin { pluginId: this.id, }) - this.defaultLocale = { - strings: { - pluginNameFacebook: 'Facebook', - }, - } + this.defaultLocale = locale + this.i18nInit() this.title = this.i18n('pluginNameFacebook') diff --git a/packages/@uppy/facebook/src/locale.js b/packages/@uppy/facebook/src/locale.js new file mode 100644 index 0000000000..b9530c2973 --- /dev/null +++ b/packages/@uppy/facebook/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + pluginNameFacebook: 'Facebook', + }, +} diff --git a/packages/@uppy/file-input/src/index.js b/packages/@uppy/file-input/src/index.js index b0b49c2929..4e733ac424 100644 --- a/packages/@uppy/file-input/src/index.js +++ b/packages/@uppy/file-input/src/index.js @@ -2,6 +2,8 @@ const { UIPlugin } = require('@uppy/core') const toArray = require('@uppy/utils/lib/toArray') const { h } = require('preact') +const locale = require('./locale') + module.exports = class FileInput extends UIPlugin { static VERSION = require('../package.json').version @@ -11,14 +13,7 @@ module.exports = class FileInput extends UIPlugin { this.title = 'File Input' this.type = 'acquirer' - this.defaultLocale = { - strings: { - // The same key is used for the same purpose by @uppy/robodog's `form()` API, but our - // locale pack scripts can't access it in Robodog. If it is updated here, it should - // also be updated there! - chooseFiles: 'Choose files', - }, - } + this.defaultLocale = locale // Default options const defaultOptions = { diff --git a/packages/@uppy/file-input/src/locale.js b/packages/@uppy/file-input/src/locale.js new file mode 100644 index 0000000000..9c0fef83b4 --- /dev/null +++ b/packages/@uppy/file-input/src/locale.js @@ -0,0 +1,8 @@ +module.exports = { + strings: { + // The same key is used for the same purpose by @uppy/robodog's `form()` API, but our + // locale pack scripts can't access it in Robodog. If it is updated here, it should + // also be updated there! + chooseFiles: 'Choose files', + }, +} diff --git a/packages/@uppy/google-drive/src/index.js b/packages/@uppy/google-drive/src/index.js index 849ae1c374..2ffbf2f67c 100644 --- a/packages/@uppy/google-drive/src/index.js +++ b/packages/@uppy/google-drive/src/index.js @@ -3,6 +3,8 @@ const { Provider } = require('@uppy/companion-client') const { h } = require('preact') const DriveProviderViews = require('./DriveProviderViews') +const locale = require('./locale') + module.exports = class GoogleDrive extends UIPlugin { static VERSION = require('../package.json').version @@ -45,11 +47,8 @@ module.exports = class GoogleDrive extends UIPlugin { pluginId: this.id, }) - this.defaultLocale = { - strings: { - pluginNameGoogleDrive: 'Google Drive', - }, - } + this.defaultLocale = locale + this.i18nInit() this.title = this.i18n('pluginNameGoogleDrive') diff --git a/packages/@uppy/google-drive/src/locale.js b/packages/@uppy/google-drive/src/locale.js new file mode 100644 index 0000000000..e4c06d82a8 --- /dev/null +++ b/packages/@uppy/google-drive/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + pluginNameGoogleDrive: 'Google Drive', + }, +} diff --git a/packages/@uppy/image-editor/src/index.js b/packages/@uppy/image-editor/src/index.js index c94eee605c..e2abf74f23 100644 --- a/packages/@uppy/image-editor/src/index.js +++ b/packages/@uppy/image-editor/src/index.js @@ -2,6 +2,8 @@ const { UIPlugin } = require('@uppy/core') const { h } = require('preact') const Editor = require('./Editor') +const locale = require('./locale.js') + module.exports = class ImageEditor extends UIPlugin { // eslint-disable-next-line global-require static VERSION = require('../package.json').version @@ -12,18 +14,7 @@ module.exports = class ImageEditor extends UIPlugin { this.title = 'Image Editor' this.type = 'editor' - this.defaultLocale = { - strings: { - revert: 'Revert', - rotate: 'Rotate', - zoomIn: 'Zoom in', - zoomOut: 'Zoom out', - flipHorizontal: 'Flip horizontal', - aspectRatioSquare: 'Crop square', - aspectRatioLandscape: 'Crop landscape (16:9)', - aspectRatioPortrait: 'Crop portrait (9:16)', - }, - } + this.defaultLocale = locale const defaultCropperOptions = { viewMode: 1, diff --git a/packages/@uppy/image-editor/src/locale.js b/packages/@uppy/image-editor/src/locale.js new file mode 100644 index 0000000000..119986f274 --- /dev/null +++ b/packages/@uppy/image-editor/src/locale.js @@ -0,0 +1,12 @@ +module.exports = { + strings: { + revert: 'Revert', + rotate: 'Rotate', + zoomIn: 'Zoom in', + zoomOut: 'Zoom out', + flipHorizontal: 'Flip horizontal', + aspectRatioSquare: 'Crop square', + aspectRatioLandscape: 'Crop landscape (16:9)', + aspectRatioPortrait: 'Crop portrait (9:16)', + }, +} diff --git a/packages/@uppy/instagram/src/index.js b/packages/@uppy/instagram/src/index.js index 4e53734f21..29c14bba86 100644 --- a/packages/@uppy/instagram/src/index.js +++ b/packages/@uppy/instagram/src/index.js @@ -3,6 +3,8 @@ const { Provider } = require('@uppy/companion-client') const { ProviderViews } = require('@uppy/provider-views') const { h } = require('preact') +const locale = require('./locale.js') + module.exports = class Instagram extends UIPlugin { static VERSION = require('../package.json').version @@ -19,11 +21,8 @@ module.exports = class Instagram extends UIPlugin { ) - this.defaultLocale = { - strings: { - pluginNameInstagram: 'Instagram', - }, - } + this.defaultLocale = locale + this.i18nInit() this.title = this.i18n('pluginNameInstagram') diff --git a/packages/@uppy/instagram/src/locale.js b/packages/@uppy/instagram/src/locale.js new file mode 100644 index 0000000000..b479118989 --- /dev/null +++ b/packages/@uppy/instagram/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + pluginNameInstagram: 'Instagram', + }, +} diff --git a/packages/@uppy/locales/src/en_US.js b/packages/@uppy/locales/src/en_US.js index eaf70c2548..751b6b276a 100644 --- a/packages/@uppy/locales/src/en_US.js +++ b/packages/@uppy/locales/src/en_US.js @@ -32,15 +32,15 @@ en_US.strings = { connectedToInternet: 'Connected to the Internet', copyLink: 'Copy link', copyLinkToClipboardFallback: 'Copy the URL below', - copyLinkToClipboardSuccess: 'Link copied to clipboard', + copyLinkToClipboardSuccess: 'Link copied to clipboard.', creatingAssembly: 'Preparing upload...', creatingAssemblyFailed: 'Transloadit: Could not create Assembly', - dashboardTitle: 'File Uploader', - dashboardWindowTitle: 'File Uploader Window (Press escape to close)', + dashboardTitle: 'Uppy Dashboard', + dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)', dataUploadedOfTotal: '%{complete} of %{total}', discardRecordedFile: 'Discard recorded file', done: 'Done', - dropHereOr: 'Drop files here or %{browse}', + dropHereOr: 'Drop here or %{browse}', dropHint: 'Drop your files here', dropPasteBoth: 'Drop files here, %{browseFiles} or %{browseFolders}', dropPasteFiles: 'Drop files here or %{browseFiles}', @@ -117,7 +117,7 @@ en_US.strings = { '0': 'We could not fully recover 1 file. Please re-select it and resume the upload.', '1': 'We could not fully recover %{smart_count} files. Please re-select them and resume the upload.', }, - removeFile: 'Remove file %{file}', + removeFile: 'Remove file', reSelect: 'Re-select', resetFilter: 'Reset filter', resume: 'Resume', diff --git a/packages/@uppy/onedrive/src/index.js b/packages/@uppy/onedrive/src/index.js index b7f4f4152c..2284abb0c0 100644 --- a/packages/@uppy/onedrive/src/index.js +++ b/packages/@uppy/onedrive/src/index.js @@ -3,6 +3,8 @@ const { Provider } = require('@uppy/companion-client') const { ProviderViews } = require('@uppy/provider-views') const { h } = require('preact') +const locale = require('./locale') + module.exports = class OneDrive extends UIPlugin { static VERSION = require('../package.json').version @@ -31,11 +33,8 @@ module.exports = class OneDrive extends UIPlugin { pluginId: this.id, }) - this.defaultLocale = { - strings: { - pluginNameOneDrive: 'OneDrive', - }, - } + this.defaultLocale = locale + this.i18nInit() this.title = this.i18n('pluginNameOneDrive') diff --git a/packages/@uppy/onedrive/src/locale.js b/packages/@uppy/onedrive/src/locale.js new file mode 100644 index 0000000000..7b0cc7e8f7 --- /dev/null +++ b/packages/@uppy/onedrive/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + pluginNameOneDrive: 'OneDrive', + }, +} diff --git a/packages/@uppy/screen-capture/src/index.js b/packages/@uppy/screen-capture/src/index.js index e99ba5e332..1eb5c92c06 100644 --- a/packages/@uppy/screen-capture/src/index.js +++ b/packages/@uppy/screen-capture/src/index.js @@ -4,6 +4,8 @@ const getFileTypeExtension = require('@uppy/utils/lib/getFileTypeExtension') const ScreenRecIcon = require('./ScreenRecIcon') const CaptureScreen = require('./CaptureScreen') +const locale = require('./locale') + // Adapted from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia function getMediaDevices () { // check if screen capturing is supported @@ -26,17 +28,7 @@ module.exports = class ScreenCapture extends UIPlugin { this.type = 'acquirer' this.icon = ScreenRecIcon - this.defaultLocale = { - strings: { - startCapturing: 'Begin screen capturing', - stopCapturing: 'Stop screen capturing', - submitRecordedFile: 'Submit recorded file', - streamActive: 'Stream active', - streamPassive: 'Stream passive', - micDisabled: 'Microphone access denied by user', - recording: 'Recording', - }, - } + this.defaultLocale = locale // set default options // https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints diff --git a/packages/@uppy/screen-capture/src/locale.js b/packages/@uppy/screen-capture/src/locale.js new file mode 100644 index 0000000000..199ed5c6c7 --- /dev/null +++ b/packages/@uppy/screen-capture/src/locale.js @@ -0,0 +1,11 @@ +module.exports = { + strings: { + startCapturing: 'Begin screen capturing', + stopCapturing: 'Stop screen capturing', + submitRecordedFile: 'Submit recorded file', + streamActive: 'Stream active', + streamPassive: 'Stream passive', + micDisabled: 'Microphone access denied by user', + recording: 'Recording', + }, +} diff --git a/packages/@uppy/status-bar/src/index.js b/packages/@uppy/status-bar/src/index.js index a15910f9f5..b714657beb 100644 --- a/packages/@uppy/status-bar/src/index.js +++ b/packages/@uppy/status-bar/src/index.js @@ -5,6 +5,8 @@ const getTextDirection = require('@uppy/utils/lib/getTextDirection') const statusBarStates = require('./StatusBarStates') const StatusBarUI = require('./StatusBar') +const locale = require('./locale.js') + /** * StatusBar: renders a status bar with upload/pause/resume/cancel/retry buttons, * progress percentage and time remaining. @@ -19,39 +21,7 @@ module.exports = class StatusBar extends UIPlugin { this.title = 'StatusBar' this.type = 'progressindicator' - this.defaultLocale = { - strings: { - uploading: 'Uploading', - upload: 'Upload', - complete: 'Complete', - uploadFailed: 'Upload failed', - paused: 'Paused', - retry: 'Retry', - retryUpload: 'Retry upload', - cancel: 'Cancel', - pause: 'Pause', - resume: 'Resume', - done: 'Done', - filesUploadedOfTotal: { - 0: '%{complete} of %{smart_count} file uploaded', - 1: '%{complete} of %{smart_count} files uploaded', - }, - dataUploadedOfTotal: '%{complete} of %{total}', - xTimeLeft: '%{time} left', - uploadXFiles: { - 0: 'Upload %{smart_count} file', - 1: 'Upload %{smart_count} files', - }, - uploadXNewFiles: { - 0: 'Upload +%{smart_count} file', - 1: 'Upload +%{smart_count} files', - }, - xMoreFilesAdded: { - 0: '%{smart_count} more file added', - 1: '%{smart_count} more files added', - }, - }, - } + this.defaultLocale = locale // set default options const defaultOptions = { diff --git a/packages/@uppy/status-bar/src/locale.js b/packages/@uppy/status-bar/src/locale.js new file mode 100644 index 0000000000..7da3099a3c --- /dev/null +++ b/packages/@uppy/status-bar/src/locale.js @@ -0,0 +1,48 @@ +module.exports = { + strings: { + // Shown in the status bar while files are being uploaded. + uploading: 'Uploading', + // Shown in the status bar once all files have been uploaded. + complete: 'Complete', + // Shown in the status bar if an upload failed. + uploadFailed: 'Upload failed', + // Shown in the status bar while the upload is paused. + paused: 'Paused', + // Used as the label for the button that retries an upload. + retry: 'Retry', + // Used as the label for the button that cancels an upload. + cancel: 'Cancel', + // Used as the label for the button that pauses an upload. + pause: 'Pause', + // Used as the label for the button that resumes an upload. + resume: 'Resume', + // Used as the label for the button that resets the upload state after an upload + done: 'Done', + // When `showProgressDetails` is set, shows the number of files that have been fully uploaded so far. + filesUploadedOfTotal: { + 0: '%{complete} of %{smart_count} file uploaded', + 1: '%{complete} of %{smart_count} files uploaded', + }, + // When `showProgressDetails` is set, shows the amount of bytes that have been uploaded so far. + dataUploadedOfTotal: '%{complete} of %{total}', + // When `showProgressDetails` is set, shows an estimation of how long the upload will take to complete. + xTimeLeft: '%{time} left', + // Used as the label for the button that starts an upload. + uploadXFiles: { + 0: 'Upload %{smart_count} file', + 1: 'Upload %{smart_count} files', + }, + // Used as the label for the button that starts an upload, if another upload has been started in the past + // and new files were added later. + uploadXNewFiles: { + 0: 'Upload +%{smart_count} file', + 1: 'Upload +%{smart_count} files', + }, + upload: 'Upload', + retryUpload: 'Retry upload', + xMoreFilesAdded: { + 0: '%{smart_count} more file added', + 1: '%{smart_count} more files added', + }, + }, +} diff --git a/packages/@uppy/thumbnail-generator/src/index.js b/packages/@uppy/thumbnail-generator/src/index.js index a9d532fe5c..0afb5fda8b 100644 --- a/packages/@uppy/thumbnail-generator/src/index.js +++ b/packages/@uppy/thumbnail-generator/src/index.js @@ -4,6 +4,8 @@ const isObjectURL = require('@uppy/utils/lib/isObjectURL') const isPreviewSupported = require('@uppy/utils/lib/isPreviewSupported') const exifr = require('exifr/dist/mini.legacy.umd.js') +const locale = require('./locale') + /** * The Thumbnail Generator plugin */ @@ -21,11 +23,7 @@ module.exports = class ThumbnailGenerator extends UIPlugin { this.defaultThumbnailDimension = 200 this.thumbnailType = this.opts.thumbnailType || 'image/jpeg' - this.defaultLocale = { - strings: { - generatingThumbnails: 'Generating thumbnails...', - }, - } + this.defaultLocale = locale const defaultOptions = { thumbnailWidth: null, diff --git a/packages/@uppy/thumbnail-generator/src/locale.js b/packages/@uppy/thumbnail-generator/src/locale.js new file mode 100644 index 0000000000..df1d0f4487 --- /dev/null +++ b/packages/@uppy/thumbnail-generator/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + generatingThumbnails: 'Generating thumbnails...', + }, +} diff --git a/packages/@uppy/transloadit/src/index.js b/packages/@uppy/transloadit/src/index.js index 81b1d58677..135f1caece 100644 --- a/packages/@uppy/transloadit/src/index.js +++ b/packages/@uppy/transloadit/src/index.js @@ -6,6 +6,8 @@ const Client = require('./Client') const AssemblyOptions = require('./AssemblyOptions') const AssemblyWatcher = require('./AssemblyWatcher') +const locale = require('./locale') + function defaultGetAssemblyOptions (file, options) { return { params: options.params, @@ -38,13 +40,7 @@ module.exports = class Transloadit extends BasePlugin { this.id = this.opts.id || 'Transloadit' this.title = 'Transloadit' - this.defaultLocale = { - strings: { - creatingAssembly: 'Preparing upload...', - creatingAssemblyFailed: 'Transloadit: Could not create Assembly', - encoding: 'Encoding...', - }, - } + this.defaultLocale = locale const defaultOptions = { service: 'https://api2.transloadit.com', diff --git a/packages/@uppy/transloadit/src/locale.js b/packages/@uppy/transloadit/src/locale.js new file mode 100644 index 0000000000..9173884a47 --- /dev/null +++ b/packages/@uppy/transloadit/src/locale.js @@ -0,0 +1,11 @@ +module.exports = { + strings: { + // Shown while Assemblies are being created for an upload. + creatingAssembly: 'Preparing upload...', + // Shown if an Assembly could not be created. + creatingAssemblyFailed: 'Transloadit: Could not create Assembly', + // Shown after uploads have succeeded, but when the Assembly is still executing. + // This only shows if `waitForMetadata` or `waitForEncoding` was enabled. + encoding: 'Encoding...', + }, +} diff --git a/packages/@uppy/url/src/index.js b/packages/@uppy/url/src/index.js index 0b0b5ce154..2213277ec1 100644 --- a/packages/@uppy/url/src/index.js +++ b/packages/@uppy/url/src/index.js @@ -5,6 +5,8 @@ const UrlUI = require('./UrlUI.js') const toArray = require('@uppy/utils/lib/toArray') const forEachDroppedOrPastedUrl = require('./utils/forEachDroppedOrPastedUrl') +const locale = require('./locale') + function UrlIcon () { return ( ) - this.defaultLocale = { - strings: { - pluginNameCamera: 'Camera', - smile: 'Smile!', - takePicture: 'Take a picture', - startRecording: 'Begin video recording', - stopRecording: 'Stop video recording', - allowAccessTitle: 'Please allow access to your camera', - allowAccessDescription: 'In order to take pictures or record video with your camera, please allow camera access for this site.', - noCameraTitle: 'Camera Not Available', - noCameraDescription: 'In order to take pictures or record video, please connect a camera device', - recordingStoppedMaxSize: 'Recording stopped because the file size is about to exceed the limit', - recordingLength: 'Recording length %{recording_length}', - submitRecordedFile: 'Submit recorded file', - discardRecordedFile: 'Discard recorded file', - }, - } + this.defaultLocale = locale // set default options const defaultOptions = { diff --git a/packages/@uppy/webcam/src/locale.js b/packages/@uppy/webcam/src/locale.js new file mode 100644 index 0000000000..35b45817e0 --- /dev/null +++ b/packages/@uppy/webcam/src/locale.js @@ -0,0 +1,28 @@ +module.exports = { + strings: { + pluginNameCamera: 'Camera', + noCameraTitle: 'Camera Not Available', + noCameraDescription: 'In order to take pictures or record video, please connect a camera device', + recordingStoppedMaxSize: 'Recording stopped because the file size is about to exceed the limit', + submitRecordedFile: 'Submit recorded file', + discardRecordedFile: 'Discard recorded file', + // Shown before a picture is taken when the `countdown` option is set. + smile: 'Smile!', + // Used as the label for the button that takes a picture. + // This is not visibly rendered but is picked up by screen readers. + takePicture: 'Take a picture', + // Used as the label for the button that starts a video recording. + // This is not visibly rendered but is picked up by screen readers. + startRecording: 'Begin video recording', + // Used as the label for the button that stops a video recording. + // This is not visibly rendered but is picked up by screen readers. + stopRecording: 'Stop video recording', + // Used as the label for the recording length counter. See the showRecordingLength option. + // This is not visibly rendered but is picked up by screen readers. + recordingLength: 'Recording length %{recording_length}', + // Title on the “allow access” screen + allowAccessTitle: 'Please allow access to your camera', + // Description on the “allow access” screen + allowAccessDescription: 'In order to take pictures or record video with your camera, please allow camera access for this site.', + }, +} diff --git a/packages/@uppy/xhr-upload/src/index.js b/packages/@uppy/xhr-upload/src/index.js index 2782a549b5..f3e2c6d90e 100644 --- a/packages/@uppy/xhr-upload/src/index.js +++ b/packages/@uppy/xhr-upload/src/index.js @@ -10,6 +10,8 @@ const { RateLimitedQueue, internalRateLimitedQueue } = require('@uppy/utils/lib/ const NetworkError = require('@uppy/utils/lib/NetworkError') const isNetworkError = require('@uppy/utils/lib/isNetworkError') +const locale = require('./locale') + function buildResponseError (xhr, err) { let error = err // No error message @@ -53,11 +55,7 @@ module.exports = class XHRUpload extends BasePlugin { this.id = this.opts.id || 'XHRUpload' this.title = 'XHRUpload' - this.defaultLocale = { - strings: { - timedOut: 'Upload stalled for %{seconds} seconds, aborting.', - }, - } + this.defaultLocale = locale // Default options const defaultOptions = { diff --git a/packages/@uppy/xhr-upload/src/locale.js b/packages/@uppy/xhr-upload/src/locale.js new file mode 100644 index 0000000000..24f6aa38fc --- /dev/null +++ b/packages/@uppy/xhr-upload/src/locale.js @@ -0,0 +1,6 @@ +module.exports = { + strings: { + // Shown in the Informer if an upload is being canceled because it stalled for too long. + timedOut: 'Upload stalled for %{seconds} seconds, aborting.', + }, +} diff --git a/packages/@uppy/zoom/src/index.js b/packages/@uppy/zoom/src/index.js index 542f5a7587..e02a964ee8 100644 --- a/packages/@uppy/zoom/src/index.js +++ b/packages/@uppy/zoom/src/index.js @@ -3,6 +3,8 @@ const { Provider } = require('@uppy/companion-client') const { ProviderViews } = require('@uppy/provider-views') const { h } = require('preact') +const locale = require('./locale') + module.exports = class Zoom extends UIPlugin { static VERSION = require('../package.json').version @@ -30,11 +32,8 @@ module.exports = class Zoom extends UIPlugin { pluginId: this.id, }) - this.defaultLocale = { - strings: { - pluginNameZoom: 'Zoom', - }, - } + this.defaultLocale = locale + this.i18nInit() this.title = this.i18n('pluginNameZoom') diff --git a/packages/@uppy/zoom/src/locale.js b/packages/@uppy/zoom/src/locale.js new file mode 100644 index 0000000000..9f090a3d5b --- /dev/null +++ b/packages/@uppy/zoom/src/locale.js @@ -0,0 +1,5 @@ +module.exports = { + strings: { + pluginNameZoom: 'Zoom', + }, +} diff --git a/private/locale-pack/helpers.mjs b/private/locale-pack/helpers.mjs new file mode 100644 index 0000000000..a0269ff277 --- /dev/null +++ b/private/locale-pack/helpers.mjs @@ -0,0 +1,22 @@ +import glob from 'glob' + +export function getPaths (globPath) { + return new Promise((resolve, reject) => { + glob(globPath, (error, paths) => { + if (error) reject(error) + else resolve(paths) + }) + }) +} + +export function sortObjectAlphabetically (obj) { + return Object.fromEntries( + Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) + ) +} + +export function omit (object, key) { + const copy = { ...object } + delete copy[key] + return copy +} diff --git a/private/locale-pack/index.mjs b/private/locale-pack/index.mjs new file mode 100644 index 0000000000..a98166d860 --- /dev/null +++ b/private/locale-pack/index.mjs @@ -0,0 +1,168 @@ +/* eslint-disable no-console, prefer-arrow-callback */ +import path from 'node:path' +import fs from 'node:fs' +import { readFile, writeFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' + +import dedent from 'dedent' +import stringifyObject from 'stringify-object' +import { remark } from 'remark' +import { headingRange } from 'mdast-util-heading-range' +import remarkFrontmatter from 'remark-frontmatter' + +import remarkConfig from '../remark-lint-uppy/index.js' + +import { getPaths, sortObjectAlphabetically } from './helpers.mjs' + +const { settings: remarkSettings } = remarkConfig + +const root = fileURLToPath(new URL('../../', import.meta.url)) + +const localesPath = path.join(root, 'packages', '@uppy', 'locales') +const templatePath = path.join(localesPath, 'template.js') +const englishLocalePath = path.join(localesPath, 'src', 'en_US.js') + +main() + .then(() => { + console.log(`✅ Generated '${englishLocalePath}'`) + console.log('✅ Generated locale docs') + console.log('✅ Generated types') + }) + .catch((error) => { + console.error(error) + process.exit(1) + }) + +function main () { + return getPaths(`${root}/packages/@uppy/**/src/locale.js`) + .then(importFiles) + .then(createCombinedLocale) + .then(({ combinedLocale, locales }) => ({ + combinedLocale: sortObjectAlphabetically(combinedLocale), + locales, + })) + .then(({ combinedLocale, locales }) => { + return readFile(templatePath, 'utf-8') + .then((fileString) => populateTemplate(fileString, combinedLocale)) + .then((file) => writeFile(englishLocalePath, file)) + .then(() => { + for (const [pluginName, locale] of Object.entries(locales)) { + generateLocaleDocs(pluginName) + generateTypes(pluginName, locale) + } + return locales + }) + }) +} + +async function importFiles (paths) { + const locales = {} + + for (const filePath of paths) { + const pluginName = path.basename(path.join(filePath, '..', '..')) + // Note: `.default` should be removed when we move to ESM + const locale = (await import(filePath)).default + + locales[pluginName] = locale + } + + return locales +} + +function createCombinedLocale (locales) { + return new Promise((resolve, reject) => { + const combinedLocale = {} + const entries = Object.entries(locales) + + for (const [pluginName, locale] of entries) { + Object.entries(locale.strings).forEach(([key, value]) => { + if (key in combinedLocale && value !== combinedLocale[key]) { + reject(new Error(`'${key}' from ${pluginName} already exists in locale pack.`)) + } + combinedLocale[key] = value + }) + } + + resolve({ combinedLocale, locales }) + }) +} + +function populateTemplate (fileString, combinedLocale) { + const formattedLocale = stringifyObject(combinedLocale, { + indent: ' ', + singleQuotes: true, + inlineCharacterLimit: 12, + }) + return fileString.replace('en_US.strings = {}', `en_US.strings = ${formattedLocale}`) +} + +function generateTypes (pluginName, locale) { + const allowedStringTypes = Object.keys(locale.strings) + .map((key) => ` | '${key}'`) + .join('\n') + const pluginClassName = pluginName + .split('-') + .map((str) => str.replace(/^\w/, (c) => c.toUpperCase())) + .join('') + + const localePath = path.join( + root, + 'packages', + '@uppy', + pluginName, + 'types', + 'generatedLocale.d.ts' + ) + + const localeTypes = dedent` + /* eslint-disable */ + import type { Locale } from '@uppy/core' + + type ${pluginClassName}Locale = Locale< + ${allowedStringTypes} + > + + export default ${pluginClassName}Locale + ` + + fs.writeFileSync(localePath, localeTypes) +} + +function generateLocaleDocs (pluginName) { + const fileName = `${pluginName}.md` + const docPath = path.join(root, 'website', 'src', 'docs', fileName) + const localePath = path.join(root, 'packages', '@uppy', pluginName, 'src', 'locale.js') + const rangeOptions = { test: 'locale: {}', ignoreFinalDefinitions: true } + + if (!fs.existsSync(docPath)) { + console.error( + `⚠️ Could not find markdown documentation file for "${pluginName}". Make sure the plugin name matches the markdown file name.` + ) + return + } + + remark() + .data('settings', remarkSettings) + .use(remarkFrontmatter) + .use(() => (tree) => { + // Replace all nodes after the locale heading until the next heading (or eof) + headingRange(tree, rangeOptions, (start, _, end) => [ + start, + { + type: 'html', + // `module.exports` is not allowed by eslint in our docs. + // The script outputs an extra newline which also isn't excepted by eslint + value: '', + }, + { + type: 'code', + lang: 'js', + meta: null, + value: fs.readFileSync(localePath, 'utf-8'), + }, + end, + ]) + }) + .process(fs.readFileSync(docPath)) + .then((file) => fs.writeFileSync(docPath, String(file))) +} diff --git a/private/locale-pack/package.json b/private/locale-pack/package.json new file mode 100644 index 0000000000..cef3d6905c --- /dev/null +++ b/private/locale-pack/package.json @@ -0,0 +1,20 @@ +{ + "private": true, + "name": "locale-pack", + "author": "Merlijn Vos ", + "description": "Generate locale pack, types, and documentation", + "main": "index.mjs", + "scripts": { + "build": "yarn node index.mjs", + "test": "yarn node test.mjs" + }, + "dependencies": { + "chalk": "^4.1.2", + "dedent": "^0.7.0", + "glob": "^7.2.0", + "mdast-util-heading-range": "^3.1.0", + "remark": "^14.0.1", + "remark-frontmatter": "^4.0.1", + "stringify-object": "^4.0.0" + } +} diff --git a/private/locale-pack/test.mjs b/private/locale-pack/test.mjs new file mode 100644 index 0000000000..2e8a19b32f --- /dev/null +++ b/private/locale-pack/test.mjs @@ -0,0 +1,159 @@ +/* eslint-disable no-console, prefer-arrow-callback */ +import path from 'node:path' +import fs from 'node:fs' +import { fileURLToPath } from 'node:url' + +import glob from 'glob' +import chalk from 'chalk' + +import { getPaths, omit } from './helpers.mjs' + +const root = fileURLToPath(new URL('../../', import.meta.url)) +const leadingLocaleName = 'en_US' +const mode = process.argv[2] +const pluginLocaleDependencies = { + core: 'provider-views', +} + +test() + .then(() => { + console.log('\n') + console.log('No blocking issues found') + }) + .catch((error) => { + console.error(error) + process.exit(1) + }) + +function test () { + switch (mode) { + case 'unused': + return getPaths(`${root}/packages/@uppy/**/src/locale.js`) + .then((paths) => paths.map((filePath) => path.basename(path.join(filePath, '..', '..')))) + .then(getAllFilesPerPlugin) + .then(unused) + + case 'warnings': + return getPaths(`${root}/packages/@uppy/locales/src/*.js`) + .then(importFiles) + .then((locales) => ({ + leadingLocale: locales[leadingLocaleName], + followerLocales: omit(locales, leadingLocaleName), + })) + .then(warnings) + + default: + return Promise.reject(new Error(`Invalid mode "${mode}"`)) + } +} + +async function importFiles (paths) { + const locales = {} + + for (const filePath of paths) { + const localeName = path.basename(filePath, '.js') + // Note: `.default` should be removed when we move to ESM + const locale = (await import(filePath)).default + + locales[localeName] = locale.strings + } + + return locales +} + +function getAllFilesPerPlugin (pluginNames) { + const filesPerPlugin = {} + + function getFiles (name) { + return glob + .sync(`${root}/packages/@uppy/${name}/lib/**/*.js`) + .filter((filePath) => !filePath.includes('locale.js')) + .map((filePath) => fs.readFileSync(filePath, 'utf-8')) + } + + for (const name of pluginNames) { + filesPerPlugin[name] = getFiles(name) + + if (name in pluginLocaleDependencies) { + filesPerPlugin[name] = filesPerPlugin[name].concat( + getFiles(pluginLocaleDependencies[name]) + ) + } + } + + return filesPerPlugin +} + +async function unused (filesPerPlugin, data) { + for (const [name, fileStrings] of Object.entries(filesPerPlugin)) { + const fileString = fileStrings.join('\n') + const localePath = path.join( + root, + 'packages', + '@uppy', + name, + 'src', + 'locale.js' + ) + const locale = (await import(localePath)).default + + for (const key of Object.keys(locale.strings)) { + const regPat = new RegExp( + `(i18n|i18nArray)\\([^\\)]*['\`"]${key}['\`"]`, + 'g' + ) + if (!fileString.match(regPat)) { + return Promise.reject(new Error(`Unused locale key "${key}" in @uppy/${name}`)) + } + } + } + + return data +} + +function warnings ({ leadingLocale, followerLocales }) { + const entries = Object.entries(followerLocales) + const logs = [] + + for (const [name, locale] of entries) { + const missing = Object.keys(leadingLocale).filter((key) => !(key in locale)) + const excess = Object.keys(locale).filter((key) => !(key in leadingLocale)) + + logs.push('\n') + logs.push(`--> Keys from ${leadingLocaleName} missing in ${name}`) + logs.push('\n') + + for (const key of missing) { + let value = leadingLocale[key] + + if (typeof value === 'object') { + // For values with plural forms, just take the first one right now + value = value[Object.keys(value)[0]] + } + + logs.push( + [ + `${chalk.cyan(name)} locale has missing string: '${chalk.red(key)}'`, + `that is present in ${chalk.cyan(leadingLocaleName)}`, + `with value: ${chalk.yellow(value)}`, + ].join(' ') + ) + } + + logs.push('\n') + logs.push(`--> Keys from ${name} missing in ${leadingLocaleName}`) + logs.push('\n') + + for (const key of excess) { + logs.push( + [ + `${chalk.cyan(name)} locale has excess string:`, + `'${chalk.yellow(key)}' that is not present`, + `in ${chalk.cyan(leadingLocaleName)}.`, + ].join(' ') + ) + } + } + + console.log(logs.join('\n')) +} diff --git a/test/endtoend/create-react-app/package-lock.json b/test/endtoend/create-react-app/package-lock.json index 4d62614926..f13f221103 100644 --- a/test/endtoend/create-react-app/package-lock.json +++ b/test/endtoend/create-react-app/package-lock.json @@ -19104,19 +19104,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -24014,8 +24001,7 @@ "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" }, "acorn-walk": { "version": "7.2.0", @@ -24079,14 +24065,12 @@ "ajv-errors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "requires": {} + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "alphanum-sort": { "version": "1.0.2", @@ -24547,8 +24531,7 @@ "babel-plugin-named-asset-import": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.7.tgz", - "integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==", - "requires": {} + "integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==" }, "babel-plugin-polyfill-corejs2": { "version": "0.2.2", @@ -27159,8 +27142,7 @@ "eslint-plugin-react-hooks": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz", - "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==", - "requires": {} + "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==" }, "eslint-plugin-testing-library": { "version": "3.10.2", @@ -29853,8 +29835,7 @@ "jest-pnp-resolver": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "requires": {} + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==" }, "jest-regex-util": { "version": "26.0.0", @@ -35830,12 +35811,6 @@ "is-typedarray": "^1.0.0" } }, - "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "peer": true - }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -37524,8 +37499,7 @@ "ws": { "version": "7.5.3", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", - "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "requires": {} + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==" }, "xml-name-validator": { "version": "3.0.0", diff --git a/website/src/docs/aws-s3.md b/website/src/docs/aws-s3.md index ee88a04e58..cfbef44c7c 100644 --- a/website/src/docs/aws-s3.md +++ b/website/src/docs/aws-s3.md @@ -115,17 +115,15 @@ This option is useful when uploading to an S3-like service that doesn’t reply ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const locales = { +module.exports = { strings: { - // Shown in the StatusBar while the upload is being signed. - preparingUpload: 'Preparing upload...', + timedOut: 'Upload stalled for %{seconds} seconds, aborting.', }, } + ``` ## S3 Bucket configuration diff --git a/website/src/docs/box.md b/website/src/docs/box.md index 4cc33f6025..18648e8cea 100644 --- a/website/src/docs/box.md +++ b/website/src/docs/box.md @@ -127,14 +127,13 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const locales = { +module.exports = { strings: { - // TODO + pluginNameBox: 'Box', }, } + ``` diff --git a/website/src/docs/uppy.md b/website/src/docs/core.md similarity index 92% rename from website/src/docs/uppy.md rename to website/src/docs/core.md index c88e386c37..22e41be1fd 100644 --- a/website/src/docs/uppy.md +++ b/website/src/docs/core.md @@ -277,57 +277,70 @@ const uppy = new Uppy({ ### `locale: {}` -This allows you to override language strings: + ```js -const uppy = new Uppy({ - // ... - locale: { - strings: { - youCanOnlyUploadX: { - 0: 'You can only upload %{smart_count} file', - 1: 'You can only upload %{smart_count} files', - }, - youHaveToAtLeastSelectX: { - 0: 'You have to select at least %{smart_count} file', - 1: 'You have to select at least %{smart_count} files', - }, - exceedsSize: 'This file exceeds maximum allowed size of %{size}', - youCanOnlyUploadFileTypes: 'You can only upload: %{types}', - companionError: 'Connection with Companion failed', +module.exports = { + strings: { + addBulkFilesFailed: { + 0: 'Failed to add %{smart_count} file due to an internal error', + 1: 'Failed to add %{smart_count} files due to internal errors', + }, + youCanOnlyUploadX: { + 0: 'You can only upload %{smart_count} file', + 1: 'You can only upload %{smart_count} files', + }, + youHaveToAtLeastSelectX: { + 0: 'You have to select at least %{smart_count} file', + 1: 'You have to select at least %{smart_count} files', + }, + exceedsSize: '%{file} exceeds maximum allowed size of %{size}', + missingRequiredMetaField: 'Missing required meta fields', + missingRequiredMetaFieldOnFile: + 'Missing required meta fields in %{fileName}', + inferiorSize: 'This file is smaller than the allowed size of %{size}', + youCanOnlyUploadFileTypes: 'You can only upload: %{types}', + noMoreFilesAllowed: 'Cannot add more files', + noDuplicates: + "Cannot add the duplicate file '%{fileName}', it already exists", + companionError: 'Connection with Companion failed', + authAborted: 'Authentication aborted', + companionUnauthorizeHint: + 'To unauthorize to your %{provider} account, please go to %{url}', + failedToUpload: 'Failed to upload %{file}', + noInternetConnection: 'No Internet connection', + connectedToInternet: 'Connected to the Internet', + // Strings for remote providers + noFilesFound: 'You have no files or folders here', + selectX: { + 0: 'Select %{smart_count}', + 1: 'Select %{smart_count}', + }, + allFilesFromFolderNamed: 'All files from folder %{name}', + openFolderNamed: 'Open folder %{name}', + cancel: 'Cancel', + logOut: 'Log out', + filter: 'Filter', + resetFilter: 'Reset filter', + loading: 'Loading...', + authenticateWithTitle: + 'Please authenticate with %{pluginName} to select files', + authenticateWith: 'Connect to %{pluginName}', + signInWithGoogle: 'Sign in with Google', + searchImages: 'Search for images', + enterTextToSearch: 'Enter text to search for images', + backToSearch: 'Back to Search', + emptyFolderAdded: 'No files were added from empty folder', + folderAlreadyAdded: 'The folder "%{folder}" was already added', + folderAdded: { + 0: 'Added %{smart_count} file from %{folder}', + 1: 'Added %{smart_count} files from %{folder}', }, }, -}) -``` - -Instead of overriding strings yourself, consider using [one of our language packs](https://github.com/transloadit/uppy/tree/master/packages/%40uppy/locales) (or contributing one!): - -```js -import russianLocale from '@uppy/locales/lib/ru_RU' -// ^-- OR: import russianLocale from '@uppy/locales/lib/ru_RU' -const uppy = new Uppy({ - locale: russianLocale, -}) -``` - -If you use Uppy from a CDN, [there’s an example](/examples/i18n/) showcasing how to change languages. - -For flexibility, you can pass a `locale` at the `Uppy`/core level, or to Plugins individually. The locale strings that you set in core take precedence. - -It also offers the pluralization function, which is used to figure which string will be used for the provided `smart_count` number. - -For example, for the Icelandic language, the pluralization function would be: +} -```js -const uppy = new Uppy({ - locale: { - pluralize: (n) => ((n % 10 !== 1 || n % 100 === 11) ? 1 : 0), - }, -}) ``` -We are using a forked [Polyglot.js](https://github.com/airbnb/polyglot.js/blob/master/index.js#L37-L60). - ### `store: defaultStore()` The Store that is used to keep track of internal state. By [default](/docs/stores/#DefaultStore), a plain object is used. diff --git a/website/src/docs/dashboard.md b/website/src/docs/dashboard.md index 6ba45f8fd2..87ddd9c537 100644 --- a/website/src/docs/dashboard.md +++ b/website/src/docs/dashboard.md @@ -333,104 +333,98 @@ Dashboard ships with the `ThumbnailGenerator` plugin that adds small resized ima ### `locale: {}` -Localize text that is shown to the user. - -The Dashboard also includes the [`@uppy/status-bar`](/docs/status-bar) plugin by default, which has its own strings. Strings for the Status Bar can also be specified in the Dashboard `locale.strings` option, and will be passed down. They are not all listed below—see the [`@uppy/status-bar`](/docs/status-bar) documentation pages for the full list. - -The default English strings are: + ```js -const strings = { - // When `inline: false`, used as the screen reader label for the button that closes the modal. - closeModal: 'Close Modal', - // Used as the screen reader label for the plus (+) button that shows the “Add more files” screen - addMoreFiles: 'Add more files', - // TODO - addingMoreFiles: 'Adding more files', - // Used as the header for import panels, e.g., “Import from Google Drive”. - importFrom: 'Import from %{name}', - // When `inline: false`, used as the screen reader label for the dashboard modal. - dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)', - // When `inline: true`, used as the screen reader label for the dashboard area. - dashboardTitle: 'Uppy Dashboard', - // Shown in the Informer when a link to a file was copied to the clipboard. - copyLinkToClipboardSuccess: 'Link copied to clipboard.', - // Used when a link cannot be copied automatically — the user has to select the text from the - // input element below this string. - copyLinkToClipboardFallback: 'Copy the URL below', - // Used as the hover title and screen reader label for buttons that copy a file link. - copyLink: 'Copy link', - // Used as the hover title and screen reader label for file source icons, e.g., “File source: Dropbox”. - fileSource: 'File source: %{name}', - // Used as the label for buttons that accept and close panels (remote providers or metadata editor) - done: 'Done', - // TODO - back: 'Back', - // Used as the screen reader label for buttons that remove a file. - removeFile: 'Remove file', - // Used as the screen reader label for buttons that open the metadata editor panel for a file. - editFile: 'Edit file', - // Shown in the panel header for the metadata editor. Rendered as “Editing image.png”. - editing: 'Editing %{file}', - // Text for a button shown on the file preview, used to edit file metadata - edit: 'Edit', - // Used as the screen reader label for the button that saves metadata edits and returns to the - // file list view. - finishEditingFile: 'Finish editing file', - // TODO - saveChanges: 'Save changes', - // Used as the label for the tab button that opens the system file selection dialog. - myDevice: 'My Device', - // Shown in the main dashboard area when no files have been selected, and one or more - // remote provider plugins are in use. %{browse} is replaced with a link that opens the system - // file selection dialog. - dropPasteImport: 'Drop files here, paste, %{browse} or import from', - // Shown in the main dashboard area when no files have been selected, and no provider - // plugins are in use. %{browse} is replaced with a link that opens the system - // file selection dialog. - dropPaste: 'Drop files here, paste or %{browse}', - // TODO - dropHint: 'Drop your files here', - // This string is clickable and opens the system file selection dialog. - browse: 'browse', - // Used as the hover text and screen reader label for file progress indicators when - // they have been fully uploaded. - uploadComplete: 'Upload complete', - // TODO - uploadPaused: 'Upload paused', - // Used as the hover text and screen reader label for the buttons to resume paused uploads. - resumeUpload: 'Resume upload', - // Used as the hover text and screen reader label for the buttons to pause uploads. - pauseUpload: 'Pause upload', - // Used as the hover text and screen reader label for the buttons to retry failed uploads. - retryUpload: 'Retry upload', - // Used as the hover text and screen reader label for the buttons to cancel uploads. - cancelUpload: 'Cancel upload', - - // Used in a title, how many files are currently selected - xFilesSelected: { - 0: '%{smart_count} file selected', - 1: '%{smart_count} files selected', - }, - // TODO - uploadingXFiles: { - 0: 'Uploading %{smart_count} file', - 1: 'Uploading %{smart_count} files', - }, - // TODO - processingXFiles: { - 0: 'Processing %{smart_count} file', - 1: 'Processing %{smart_count} files', +module.exports = { + strings: { + // When `inline: false`, used as the screen reader label for the button that closes the modal. + closeModal: 'Close Modal', + // Used as the screen reader label for the plus (+) button that shows the “Add more files” screen + addMoreFiles: 'Add more files', + addingMoreFiles: 'Adding more files', + // Used as the header for import panels, e.g., “Import from Google Drive”. + importFrom: 'Import from %{name}', + // When `inline: false`, used as the screen reader label for the dashboard modal. + dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)', + // When `inline: true`, used as the screen reader label for the dashboard area. + dashboardTitle: 'Uppy Dashboard', + // Shown in the Informer when a link to a file was copied to the clipboard. + copyLinkToClipboardSuccess: 'Link copied to clipboard.', + // Used when a link cannot be copied automatically — the user has to select the text from the + // input element below this string. + copyLinkToClipboardFallback: 'Copy the URL below', + // Used as the hover title and screen reader label for buttons that copy a file link. + copyLink: 'Copy link', + back: 'Back', + // Used as the screen reader label for buttons that remove a file. + removeFile: 'Remove file', + // Used as the screen reader label for buttons that open the metadata editor panel for a file. + editFile: 'Edit file', + // Shown in the panel header for the metadata editor. Rendered as “Editing image.png”. + editing: 'Editing %{file}', + // Used as the screen reader label for the button that saves metadata edits and returns to the + // file list view. + finishEditingFile: 'Finish editing file', + saveChanges: 'Save changes', + // Used as the label for the tab button that opens the system file selection dialog. + myDevice: 'My Device', + dropHint: 'Drop your files here', + // Used as the hover text and screen reader label for file progress indicators when + // they have been fully uploaded. + uploadComplete: 'Upload complete', + uploadPaused: 'Upload paused', + // Used as the hover text and screen reader label for the buttons to resume paused uploads. + resumeUpload: 'Resume upload', + // Used as the hover text and screen reader label for the buttons to pause uploads. + pauseUpload: 'Pause upload', + // Used as the hover text and screen reader label for the buttons to retry failed uploads. + retryUpload: 'Retry upload', + // Used as the hover text and screen reader label for the buttons to cancel uploads. + cancelUpload: 'Cancel upload', + // Used in a title, how many files are currently selected + xFilesSelected: { + 0: '%{smart_count} file selected', + 1: '%{smart_count} files selected', + }, + uploadingXFiles: { + 0: 'Uploading %{smart_count} file', + 1: 'Uploading %{smart_count} files', + }, + processingXFiles: { + 0: 'Processing %{smart_count} file', + 1: 'Processing %{smart_count} files', + }, + // The "powered by Uppy" link at the bottom of the Dashboard. + poweredBy: 'Powered by %{uppy}', + addMore: 'Add more', + editFileWithFilename: 'Edit file %{file}', + save: 'Save', + cancel: 'Cancel', + dropPasteFiles: 'Drop files here or %{browseFiles}', + dropPasteFolders: 'Drop files here or %{browseFolders}', + dropPasteBoth: 'Drop files here, %{browseFiles} or %{browseFolders}', + dropPasteImportFiles: 'Drop files here, %{browseFiles} or import from:', + dropPasteImportFolders: 'Drop files here, %{browseFolders} or import from:', + dropPasteImportBoth: + 'Drop files here, %{browseFiles}, %{browseFolders} or import from:', + importFiles: 'Import files from:', + browseFiles: 'browse files', + browseFolders: 'browse folders', + recoveredXFiles: { + 0: 'We could not fully recover 1 file. Please re-select it and resume the upload.', + 1: 'We could not fully recover %{smart_count} files. Please re-select them and resume the upload.', + }, + recoveredAllFiles: 'We restored all files. You can now resume the upload.', + sessionRestored: 'Session restored', + reSelect: 'Re-select', + missingRequiredMetaFields: { + 0: 'Missing required meta field: %{fields}.', + 1: 'Missing required meta fields: %{fields}.', + }, }, - - // The "powered by Uppy" link at the bottom of the Dashboard. - poweredBy: 'Powered by %{uppy}', - - // @uppy/status-bar strings: - uploading: 'Uploading', - complete: 'Complete', - // ...etc } + ``` ### `theme: 'light'` diff --git a/website/src/docs/dragdrop.md b/website/src/docs/drag-drop.md similarity index 90% rename from website/src/docs/dragdrop.md rename to website/src/docs/drag-drop.md index f28a8f07b7..1e432aea79 100644 --- a/website/src/docs/dragdrop.md +++ b/website/src/docs/drag-drop.md @@ -86,7 +86,20 @@ Optionally, specify a string of text that explains something about the upload fo ### `locale: {}` -Localize text that is shown to the user. + + +```js +module.exports = { + strings: { + // Text to show on the droppable area. + // `%{browse}` is replaced with a link that opens the system file selection dialog. + dropHereOr: 'Drop here or %{browse}', + // Used as the label for the link that opens the system file selection dialog. + browse: 'browse', + }, +} + +``` ### `onDragOver(event)` diff --git a/website/src/docs/dropbox.md b/website/src/docs/dropbox.md index c4a7b352b4..f898684ff0 100644 --- a/website/src/docs/dropbox.md +++ b/website/src/docs/dropbox.md @@ -127,14 +127,13 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const locale = { +module.exports = { strings: { - // TODO + pluginNameDropbox: 'Dropbox', }, } + ``` diff --git a/website/src/docs/facebook.md b/website/src/docs/facebook.md index 79b9a31a6c..540bf2916e 100644 --- a/website/src/docs/facebook.md +++ b/website/src/docs/facebook.md @@ -86,14 +86,13 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const locale = { +module.exports = { strings: { - // TODO + pluginNameFacebook: 'Facebook', }, } + ``` diff --git a/website/src/docs/fileinput.md b/website/src/docs/file-input.md similarity index 92% rename from website/src/docs/fileinput.md rename to website/src/docs/file-input.md index 21aa49111c..6ebe958b5d 100644 --- a/website/src/docs/fileinput.md +++ b/website/src/docs/file-input.md @@ -84,14 +84,18 @@ The `name` attribute for the `` element. ### `locale: {}` -When `pretty` is set, specify a custom label for the button. + ```js -const locale = { +module.exports = { strings: { + // The same key is used for the same purpose by @uppy/robodog's `form()` API, but our + // locale pack scripts can't access it in Robodog. If it is updated here, it should + // also be updated there! chooseFiles: 'Choose files', }, } + ``` ## Custom file input diff --git a/website/src/docs/google-drive.md b/website/src/docs/google-drive.md index ab56cfb3cc..dabb2e830e 100644 --- a/website/src/docs/google-drive.md +++ b/website/src/docs/google-drive.md @@ -122,14 +122,13 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const locale = { - strings:{ - // TODO +module.exports = { + strings: { + pluginNameGoogleDrive: 'Google Drive', }, } + ``` diff --git a/website/src/docs/instagram.md b/website/src/docs/instagram.md index c35995f79b..72a2cf98bb 100644 --- a/website/src/docs/instagram.md +++ b/website/src/docs/instagram.md @@ -92,14 +92,13 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const locale = { +module.exports = { strings: { - // TODO + pluginNameInstagram: 'Instagram', }, } + ``` diff --git a/website/src/docs/onedrive.md b/website/src/docs/onedrive.md index 28fe5bc123..34903da5ad 100644 --- a/website/src/docs/onedrive.md +++ b/website/src/docs/onedrive.md @@ -86,14 +86,13 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const locale = { +module.exports = { strings: { - // TODO + pluginNameOneDrive: 'OneDrive', }, } + ``` diff --git a/website/src/docs/screen-capture.md b/website/src/docs/screen-capture.md index ea9a819dcd..60b1d7fcdf 100644 --- a/website/src/docs/screen-capture.md +++ b/website/src/docs/screen-capture.md @@ -101,3 +101,20 @@ Set the preferred mime type for video recordings, for example `'video/webm'`. If If no preferred video mime type is given, the ScreenCapture plugin will prefer types listed in the [`allowedFileTypes` restriction](/docs/uppy/#restrictions), if any. ### `locale: {}` + + + +```js +module.exports = { + strings: { + startCapturing: 'Begin screen capturing', + stopCapturing: 'Stop screen capturing', + submitRecordedFile: 'Submit recorded file', + streamActive: 'Stream active', + streamPassive: 'Stream passive', + micDisabled: 'Microphone access denied by user', + recording: 'Recording', + }, +} + +``` diff --git a/website/src/docs/statusbar.md b/website/src/docs/status-bar.md similarity index 66% rename from website/src/docs/statusbar.md rename to website/src/docs/status-bar.md index bffbd7863c..8824489bf5 100644 --- a/website/src/docs/statusbar.md +++ b/website/src/docs/status-bar.md @@ -120,51 +120,58 @@ const doneButtonHandler = () => { ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const strings = { - // Shown in the status bar while files are being uploaded. - uploading: 'Uploading', - // Shown in the status bar once all files have been uploaded. - complete: 'Complete', - // Shown in the status bar if an upload failed. - uploadFailed: 'Upload failed', - // Shown in the status bar while the upload is paused. - paused: 'Paused', - // Used as the label for the button that retries an upload. - retry: 'Retry', - // Used as the label for the button that cancels an upload. - cancel: 'Cancel', - // Used as the label for the button that pauses an upload. - pause: 'Pause', - // Used as the label for the button that resumes an upload. - resume: 'Resume', - // Used as the label for the button that resets the upload state after an upload - done: 'Done', - // When `showProgressDetails` is set, shows the number of files that have been fully uploaded so far. - filesUploadedOfTotal: { - 0: '%{complete} of %{smart_count} file uploaded', - 1: '%{complete} of %{smart_count} files uploaded', - }, - // When `showProgressDetails` is set, shows the amount of bytes that have been uploaded so far. - dataUploadedOfTotal: '%{complete} of %{total}', - // When `showProgressDetails` is set, shows an estimation of how long the upload will take to complete. - xTimeLeft: '%{time} left', - // Used as the label for the button that starts an upload. - uploadXFiles: { - 0: 'Upload %{smart_count} file', - 1: 'Upload %{smart_count} files', - }, - // Used as the label for the button that starts an upload, if another upload has been started in the past - // and new files were added later. - uploadXNewFiles: { - 0: 'Upload +%{smart_count} file', - 1: 'Upload +%{smart_count} files', +module.exports = { + strings: { + // Shown in the status bar while files are being uploaded. + uploading: 'Uploading', + // Shown in the status bar once all files have been uploaded. + complete: 'Complete', + // Shown in the status bar if an upload failed. + uploadFailed: 'Upload failed', + // Shown in the status bar while the upload is paused. + paused: 'Paused', + // Used as the label for the button that retries an upload. + retry: 'Retry', + // Used as the label for the button that cancels an upload. + cancel: 'Cancel', + // Used as the label for the button that pauses an upload. + pause: 'Pause', + // Used as the label for the button that resumes an upload. + resume: 'Resume', + // Used as the label for the button that resets the upload state after an upload + done: 'Done', + // When `showProgressDetails` is set, shows the number of files that have been fully uploaded so far. + filesUploadedOfTotal: { + 0: '%{complete} of %{smart_count} file uploaded', + 1: '%{complete} of %{smart_count} files uploaded', + }, + // When `showProgressDetails` is set, shows the amount of bytes that have been uploaded so far. + dataUploadedOfTotal: '%{complete} of %{total}', + // When `showProgressDetails` is set, shows an estimation of how long the upload will take to complete. + xTimeLeft: '%{time} left', + // Used as the label for the button that starts an upload. + uploadXFiles: { + 0: 'Upload %{smart_count} file', + 1: 'Upload %{smart_count} files', + }, + // Used as the label for the button that starts an upload, if another upload has been started in the past + // and new files were added later. + uploadXNewFiles: { + 0: 'Upload +%{smart_count} file', + 1: 'Upload +%{smart_count} files', + }, + upload: 'Upload', + retryUpload: 'Retry upload', + xMoreFilesAdded: { + 0: '%{smart_count} more file added', + 1: '%{smart_count} more files added', + }, }, } + ``` [`@uppy/file-input`]: /docs/file-input diff --git a/website/src/docs/transloadit.md b/website/src/docs/transloadit.md index 73a16cf1e6..5559fb9532 100644 --- a/website/src/docs/transloadit.md +++ b/website/src/docs/transloadit.md @@ -275,20 +275,21 @@ Limit the amount of uploads going on at the same time. Setting this to `0` means ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const strings = { - // Shown while Assemblies are being created for an upload. - creatingAssembly: 'Preparing upload...', - // Shown if an Assembly could not be created. - creatingAssemblyFailed: 'Transloadit: Could not create Assembly', - // Shown after uploads have succeeded, but when the Assembly is still executing. - // This only shows if `waitForMetadata` or `waitForEncoding` was enabled. - encoding: 'Encoding...', +module.exports = { + strings: { + // Shown while Assemblies are being created for an upload. + creatingAssembly: 'Preparing upload...', + // Shown if an Assembly could not be created. + creatingAssemblyFailed: 'Transloadit: Could not create Assembly', + // Shown after uploads have succeeded, but when the Assembly is still executing. + // This only shows if `waitForMetadata` or `waitForEncoding` was enabled. + encoding: 'Encoding...', + }, } + ``` ## Errors diff --git a/website/src/docs/url.md b/website/src/docs/url.md index d4ced72267..a2853e493a 100644 --- a/website/src/docs/url.md +++ b/website/src/docs/url.md @@ -84,21 +84,22 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const strings = { - // Label for the "Import" button. - import: 'Import', - // Placeholder text for the URL input. - enterUrlToImport: 'Enter URL to import a file', - // Error message shown if Companion could not load a URL. - failedToFetch: 'Companion failed to fetch this URL, please make sure it’s correct', - // Error message shown if the input does not look like a URL. - enterCorrectUrl: 'Incorrect URL: Please make sure you are entering a direct link to a file', +module.exports = { + strings: { + // Label for the "Import" button. + import: 'Import', + // Placeholder text for the URL input. + enterUrlToImport: 'Enter URL to import a file', + // Error message shown if Companion could not load a URL. + failedToFetch: 'Companion failed to fetch this URL, please make sure it’s correct', + // Error message shown if the input does not look like a URL. + enterCorrectUrl: 'Incorrect URL: Please make sure you are entering a direct link to a file', + }, } + ``` ## Methods diff --git a/website/src/docs/webcam.md b/website/src/docs/webcam.md index 90d10a26d8..46814badfb 100644 --- a/website/src/docs/webcam.md +++ b/website/src/docs/webcam.md @@ -150,29 +150,36 @@ If no preferred image mime type is given, the Webcam plugin will prefer types li ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const strings = { - // Shown before a picture is taken when the `countdown` option is set. - smile: 'Smile!', - // Used as the label for the button that takes a picture. - // This is not visibly rendered but is picked up by screen readers. - takePicture: 'Take a picture', - // Used as the label for the button that starts a video recording. - // This is not visibly rendered but is picked up by screen readers. - startRecording: 'Begin video recording', - // Used as the label for the button that stops a video recording. - // This is not visibly rendered but is picked up by screen readers. - stopRecording: 'Stop video recording', - // Used as the label for the recording length counter. See the showRecordingLength option. - // This is not visibly rendered but is picked up by screen readers. - recordingLength: 'Recording length %{recording_length}', - // Title on the “allow access” screen - allowAccessTitle: 'Please allow access to your camera', - // Description on the “allow access” screen - allowAccessDescription: 'In order to take pictures or record video with your camera, please allow camera access for this site.', +module.exports = { + strings: { + pluginNameCamera: 'Camera', + noCameraTitle: 'Camera Not Available', + noCameraDescription: 'In order to take pictures or record video, please connect a camera device', + recordingStoppedMaxSize: 'Recording stopped because the file size is about to exceed the limit', + submitRecordedFile: 'Submit recorded file', + discardRecordedFile: 'Discard recorded file', + // Shown before a picture is taken when the `countdown` option is set. + smile: 'Smile!', + // Used as the label for the button that takes a picture. + // This is not visibly rendered but is picked up by screen readers. + takePicture: 'Take a picture', + // Used as the label for the button that starts a video recording. + // This is not visibly rendered but is picked up by screen readers. + startRecording: 'Begin video recording', + // Used as the label for the button that stops a video recording. + // This is not visibly rendered but is picked up by screen readers. + stopRecording: 'Stop video recording', + // Used as the label for the recording length counter. See the showRecordingLength option. + // This is not visibly rendered but is picked up by screen readers. + recordingLength: 'Recording length %{recording_length}', + // Title on the “allow access” screen + allowAccessTitle: 'Please allow access to your camera', + // Description on the “allow access” screen + allowAccessDescription: 'In order to take pictures or record video with your camera, please allow camera access for this site.', + }, } + ``` diff --git a/website/src/docs/xhrupload.md b/website/src/docs/xhr-upload.md similarity index 97% rename from website/src/docs/xhrupload.md rename to website/src/docs/xhr-upload.md index 8bacab1f11..2b7fb6ab93 100644 --- a/website/src/docs/xhrupload.md +++ b/website/src/docs/xhr-upload.md @@ -231,15 +231,16 @@ Indicates whether cross-site Access-Control requests should be made using creden ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const strings = { - // Shown in the Informer if an upload is being canceled because it stalled for too long. - timedOut: 'Upload stalled for %{seconds} seconds, aborting.', +module.exports = { + strings: { + // Shown in the Informer if an upload is being canceled because it stalled for too long. + timedOut: 'Upload stalled for %{seconds} seconds, aborting.', + }, } + ``` ## POST Parameters / Form Fields diff --git a/website/src/docs/zoom.md b/website/src/docs/zoom.md index e2726138a0..f871be6bff 100644 --- a/website/src/docs/zoom.md +++ b/website/src/docs/zoom.md @@ -90,14 +90,15 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ### `locale: {}` -Localize text that is shown to the user. - -The default English strings are: + ```js -const strings = { - // TODO +module.exports = { + strings: { + pluginNameZoom: 'Zoom', + }, } + ``` ## Zoom Marketplace diff --git a/yarn.lock b/yarn.lock index 3a760ad141..9582b8aa03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21451,7 +21451,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"get-own-enumerable-property-symbols@npm:^3.0.0": +"get-own-enumerable-property-symbols@npm:^3.0.0, get-own-enumerable-property-symbols@npm:^3.0.2": version: 3.0.2 resolution: "get-own-enumerable-property-symbols@npm:3.0.2" checksum: 8f0331f14159f939830884799f937343c8c0a2c330506094bc12cbee3665d88337fe97a4ea35c002cc2bdba0f5d9975ad7ec3abb925015cdf2a93e76d4759ede @@ -21813,7 +21813,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": +"glob@npm:^7.0.0, glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -24625,6 +24625,13 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard +"is-obj@npm:^3.0.0": + version: 3.0.0 + resolution: "is-obj@npm:3.0.0" + checksum: 75e97a99ed0b0884778887f8e913791864151307774914283b068b06b57ca86f695b024aa1ba5ed04411918edef93e2bfd8f84d68c6b6aab417802cc76f5061b + languageName: node + linkType: hard + "is-object@npm:^1.0.1": version: 1.0.2 resolution: "is-object@npm:1.0.2" @@ -24787,6 +24794,13 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard +"is-regexp@npm:^3.0.0": + version: 3.0.0 + resolution: "is-regexp@npm:3.0.0" + checksum: c0f32f93accb9408ce1fd3d47f43648b8612d28186638b3a46e835fb147842b220b2ede4bb86315e996a4d78c393b30b0b9ab9348f9be7ab27d4c2b102469c7c + languageName: node + linkType: hard + "is-resolvable@npm:^1.0.0, is-resolvable@npm:^1.1.0": version: 1.1.0 resolution: "is-resolvable@npm:1.1.0" @@ -27325,6 +27339,20 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard +"locale-pack@workspace:private/locale-pack": + version: 0.0.0-use.local + resolution: "locale-pack@workspace:private/locale-pack" + dependencies: + chalk: ^4.1.2 + dedent: ^0.7.0 + glob: ^7.2.0 + mdast-util-heading-range: ^3.1.0 + remark: ^14.0.1 + remark-frontmatter: ^4.0.1 + stringify-object: ^4.0.0 + languageName: unknown + linkType: soft + "localtunnel@npm:^2.0.1": version: 2.0.2 resolution: "localtunnel@npm:2.0.2" @@ -28429,6 +28457,17 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard +"mdast-util-heading-range@npm:^3.1.0": + version: 3.1.0 + resolution: "mdast-util-heading-range@npm:3.1.0" + dependencies: + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + mdast-util-to-string: ^3.0.0 + checksum: c0f9892cdd36ab88b368f636270e7e846edc3c923a395b6ae49913e99c88cdff9eb5b2b131a9f20e8a78d22aff66e0523acda630721f30d8e32159ca6d35ac5d + languageName: node + linkType: hard + "mdast-util-heading-style@npm:^2.0.0": version: 2.0.0 resolution: "mdast-util-heading-style@npm:2.0.0" @@ -36261,6 +36300,18 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard +"remark-frontmatter@npm:^4.0.1": + version: 4.0.1 + resolution: "remark-frontmatter@npm:4.0.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-frontmatter: ^1.0.0 + micromark-extension-frontmatter: ^1.0.0 + unified: ^10.0.0 + checksum: c1c448923cd0239e9eeafb42d7129c05081c9a1bca4c8164b562cbb748e80d103bfd058597a48d54000ce3c776200ab8ccd64a9679d955423f07e4a4e77f10c3 + languageName: node + linkType: hard + "remark-lint-emphasis-marker@npm:^3.0.0": version: 3.1.0 resolution: "remark-lint-emphasis-marker@npm:3.1.0" @@ -36697,7 +36748,7 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"remark@npm:^14.0.0": +"remark@npm:^14.0.0, remark@npm:^14.0.1": version: 14.0.1 resolution: "remark@npm:14.0.1" dependencies: @@ -39679,6 +39730,17 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard +"stringify-object@npm:^4.0.0": + version: 4.0.0 + resolution: "stringify-object@npm:4.0.0" + dependencies: + get-own-enumerable-property-symbols: ^3.0.2 + is-obj: ^3.0.0 + is-regexp: ^3.0.0 + checksum: 8fffae04044f0a4f7fa70e05b857750e35a54e6117cdf69cad2d1ef318ff4b0c8fec57a7731646fca0ca7c07ae723cb12edbbbc863377607aec9c83adb9ff5a3 + languageName: node + linkType: hard + "strip-ansi@npm:6.0.0": version: 6.0.0 resolution: "strip-ansi@npm:6.0.0"