TypeScript plugins
A TypeScript plugin is an object with a name and the capabilities it adds. definePlugin and definePreset from x18n give it types. They return their argument unchanged.
A check
This plugin adds a check that reports messages that still contain TODO:
import { definePlugin } from '@x18n/core'
export default definePlugin({
name: 'no-todo',
checks: [{
id: 'no-todo',
defaultSeverity: 'warning',
description: 'A message that still contains TODO.',
* run(ctx) {
for (const message of ctx.messages()) {
if (typeof message.value === 'string' && message.value.includes('TODO'))
yield { key: message.key, locale: message.locale, message: `"${message.key}" in ${message.locale} contains TODO.` }
}
},
}],
})import { defineConfig } from '@x18n/core'
import noTodo from './x18n/no-todo'
export default defineConfig({
plugins: [noTodo],
checks: {
'no-todo': 'error',
},
})A check is a generator. Each value it yields becomes a diagnostic. Without a location, the diagnostic points at the message of key in locale. The check id is also the diagnostic code, so x18n check --only no-todo and checks: { 'no-todo': 'off' } work as for the built-in checks.
Options
Pass a function to definePlugin to take options. The function returns the plugin:
import { definePlugin } from '@x18n/core'
export default definePlugin((options: { word: string }) => ({
name: 'no-word',
checks: [{
id: 'no-word',
defaultSeverity: 'warning',
* run(ctx) {
for (const message of ctx.messages()) {
if (typeof message.value === 'string' && message.value.includes(options.word))
yield { key: message.key, locale: message.locale, message: `"${message.key}" contains ${options.word}.` }
}
},
}],
}))In a TS config, call the function: plugins: [noWord({ word: 'FIXME' })]. In a YAML or JSON config, pass the options as the second item of a tuple. x18n imports the module and calls its default export with them:
plugins:
- [./x18n/no-word.ts, {word: FIXME}]A preset
A preset groups plugins and decides with detect when they apply. Use one when your package supports a library that x18n can detect:
import { definePlugin, definePreset } from '@x18n/core'
const calls = definePlugin({
name: 'acme-calls',
config(config) {
config.usage = {
...config.usage,
calls: [...config.usage.calls, { callee: ['translate', 'i18n.translate'] }],
}
},
})
export default definePreset({
name: 'acme-i18n',
detect: ctx => ctx.hasDependency('@acme/i18n'),
plugins: [calls],
})The config hook changes a draft of the config before it is final. Presets use it to set defaults: the config of the project wins over them. detect receives the dependencies of the project and can test files with ctx.fileExists(glob).
x18n runs detect for a preset inside the plugins of another preset. A preset that you list in presets or plugins always applies.
Typed config keys
A plugin can add types for its check ids and its options through declaration merging. defineConfig then completes them:
declare module '@x18n/core' {
interface CheckIds {
'no-todo': true
}
interface PluginConfigs {
acme: { strict?: boolean }
}
}Users then set the options under pluginConfigs, and the plugin reads them in configResolved:
import { defineConfig } from '@x18n/core'
export default defineConfig({
pluginConfigs: {
acme: { strict: true },
},
})Types
x18n exports defineConfig, definePlugin, definePreset, and the types Plugin, Preset, Check, Format, LocaleFinder, Scanner, Reporter and UserConfig. The full plugin API, with every context type, is in @x18n/core. Capabilities describes each part.