Skip to content

Repository files navigation

Rclone i18n

Translate rclone RC responses on the fly

Rewrites translatable strings in config/providers and options/info responses · Pairs with rclone-sdk · Works with Vanilla Fetch

Install

npm install rclone-i18n

The package ships a single translate function. Pass it either a raw Response or a parsed JSON body, and it returns the same shape with all known translatable fields rewritten into the target language.

Loading translations

Two modes, pick whichever suits you.

Fetch from jsDelivr (lang)

import { translate } from 'rclone-i18n'

const translated = await translate(body, { lang: 'fr' })

The language pack is fetched once from jsDelivr with cache: 'force-cache', so subsequent calls are served from the HTTP cache. Supported codes: fr, ja, zh, es.

Bring your own JSON (messages)

import { translate } from 'rclone-i18n'

const fr = await fetch('/locales/fr.json').then((r) => r.json())

const translated = await translate(body, { messages: fr })

Useful when you want to bundle the language pack, ship it from your own CDN, or override individual strings. Grab the source files from languages/ in this repo.

Vanilla Fetch

Pass the raw Response and get a Response back, with the same status, statusText, and headers:

import { translate } from 'rclone-i18n'

const res = await fetch('http://localhost:5572/config/providers', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: '{}',
})

const translatedRes = await translate(res, { lang: 'fr' })
const { providers } = await translatedRes.json()

console.log(providers.find((p) => p.Name === 'local').Description)
// → "Disque local"

Or pass the parsed body directly:

const body = await res.json()
const translated = await translate(body, { lang: 'fr' })

The original input is never mutated — bodies are deep-cloned, and a fresh Response is returned.

With rclone-sdk

rclone-sdk returns { data, error, response } from each call. Translate data directly:

import createRCDClient from 'rclone-sdk'
import { translate } from 'rclone-i18n'

const rcd = createRCDClient({ baseUrl: 'http://localhost:5572' })

const { data, error } = await rcd.POST('/config/providers')
if (error || !data) throw error

const translated = await translate(data, { lang: 'fr' })

for (const provider of translated.providers ?? []) {
    console.log(provider.Name, '→', provider.Description)
}

Same thing with /options/info:

const { data } = await rcd.POST('/options/info')
const translated = await translate(data, { lang: 'fr' })

const filter = translated.filter
const maxSize = filter?.find((o) => o.Name === 'max_size')
console.log(maxSize?.Help)
// → "Ne transférer que les fichiers plus petits que cette valeur, …"

What gets translated

translate walks the response and rewrites these fields when a matching entry exists in the language pack:

  • /config/providers
    • providers[].Description
    • providers[].Options[].Help (with Provider override)
    • providers[].Options[].Examples[].Help (incl. empty Value: "")
    • providers[].CommandHelp[].Short and .Long
    • providers[].MetadataInfo.Help and MetadataInfo.System[k].Help
  • /options/info
    • <block>[].Help (e.g. filter, main, vfs, …)
    • <block>[].Examples[].Help

Any other field is passed through unchanged. The function also returns the input untouched when:

  • the input is a Response with !ok status, or non-JSON Content-Type
  • the input is a parsed body shaped like an rclone error ({ error: string, … })

Contributing translations

Language packs live in languages/. The English source-of-truth (en.json) is regenerated from a live rclone daemon:

# rclone rcd --rc-no-auth --rc-addr 127.0.0.1:5572 &
npm run extract:i18n

scripts/check-i18n.ts (run via npm run check:i18n) compares the live extraction against languages/en.json and fails CI if keys drift. Two manual workflows wrap it: check-release runs against the latest released rclone, check-build builds rclone from master.

To add a new translation, copy languages/en.json, translate the leaf strings, and open a PR. Keys must match exactly — __empty__ is the sentinel for examples whose Value is "".

Contributing

Contributions = welcome!



Made with ☁️ for the rclone community

About

A translation layer for rclone rc responses

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages