Karelia TranslateAI translation of posts for Flarum 2 forums

Documentation

Getting started, features and limits of Karelia Translate.

On this page
  1. What you need
  2. Install in three steps
  3. Set it up in two minutes
  4. How readers use it
  5. Choose a translation service
  6. Settings
  7. Saved translations and cost
  8. Language detection
  9. What is sent where
  10. When something goes wrong
  11. Remove the extension
  12. Need help?

What you need

  • Flarum 2.0 or later, on PHP 8.3 or later.
  • Shell access to run Composer and php flarum on the server.
  • An API key for one of the translation services, or a local model. See Choose a translation service.

Install in three steps

  1. Subscribe on Floxum and follow the instructions on your Floxum subscriptions page to give Composer access to Floxum's package repository.
  2. On the server, in the folder where Flarum is installed, run:
composer require karelialabs/flarum-translate
php flarum migrate
php flarum cache:clear
  1. In the admin panel, open Extensions, enable Karelia Translate and open its settings.

Set it up in two minutes

  1. Under Translation service, choose a service. Only the fields that service needs are shown.
  2. Paste the API key and, for an OpenAI-compatible service, the address and the model name.
  3. Choose Save Changes, then Test the connection. It translates one sentence with your saved settings and shows the result, the time it took and the service that answered.
  4. Under Permissions, check who may use Translate posts. Members have it by default. Guests do not, so bots cannot spend your budget.
  5. Optional: find the language of posts written before the extension was installed. This runs on your server and costs nothing:
php flarum karelia-translate:detect

Add --all to detect the language of every post again, for example after a large import.

How readers use it

Under every post written in another language than the reader's, a small bar shows the language of the post and a link such as Translate to English. One click shows the translation under the original. Hide translation folds it away, and Show translation brings it back without asking the service again.

The first post of a discussion is translated together with the discussion title. Every translation ends with a credit line, for example "Translated from Portuguese by DeepL". Translations from OpenAI-compatible services are credited to "AI", because model names mean little to readers.

The link is not shown:

  • on posts that are already in the reader's language;
  • on posts whose language could not be detected, unless Offer translation when the language is unknown is on;
  • to readers who do not have the Translate posts permission;
  • while a post is being edited.

Choose a translation service

ServiceGood forWhat you need
DeepLThe best quality for European languages. DeepL API Free includes 500,000 characters a monthA DeepL API key. Free keys end with :fx and work too
Google Cloud TranslationThe widest list of languagesA Google Cloud API key with the Cloud Translation API enabled
Anthropic ClaudeNatural translations of slang and informal postsAn Anthropic API key. The model is set under Claude model
OpenAI-compatibleOpenAI, Mistral, Gemini, DeepSeek, OpenRouter, and local models in LM Studio, Ollama or llama.cppThe address, the model name and, except for local models, a key
LibreTranslateFree translation on your own serverThe address of your LibreTranslate server, and a key if it requires one

Addresses for OpenAI-compatible services

ServiceAddress
OpenAIhttps://api.openai.com/v1
Mistralhttps://api.mistral.ai/v1
Geminihttps://generativelanguage.googleapis.com/v1beta/openai
OpenRouterhttps://openrouter.ai/api/v1
LM Studio on the same serverhttp://127.0.0.1:1234/v1
Ollama on the same serverhttp://127.0.0.1:11434/v1

Use the model name exactly as the service writes it. Translation does not need the largest model; a small, fast one is usually enough. For local reasoning models (for example Gemma or Qwen in LM Studio), turn on Turn off model thinking if translations come back empty.

LibreTranslate with Docker

LibreTranslate is open source and keeps every word on your server. Load only the languages your forum needs, so it starts faster and uses less memory:

docker run -d --name libretranslate -p 127.0.0.1:5000:5000 \
  -e LT_LOAD_ONLY=en,sv,de \
  -v libretranslate-models:/home/libretranslate/.local \
  libretranslate/libretranslate

Then set LibreTranslate address to http://127.0.0.1:5000. The first start downloads the language models and takes a few minutes. Quality is below DeepL and the large AI models, but it costs nothing.

Claude models

The default model is claude-opus-5-5. claude-haiku-5-5 costs a fraction of that and is usually good enough for translation.

Settings

SettingWhat it does
Translation serviceThe service that translates posts. Only its own fields are shown
Translate intoThe reader's forum language (default) or The reader's browser language. With only one language pack installed, the browser language lets readers translate into their own language anyway
Character limit per monthTranslation stops for the rest of the month when the limit is reached. Default 500,000. 0 means no limit
New translations per reader per hourDefault 30. Translations that are already saved do not count, and admins have no limit. 0 means no limit
Offer translation when the language is unknownVery short posts often have no detected language. Turn on to show the link on them too
Never translateOne word or name per line, for example your forum's name or product names. They are sent as placeholders and come back exactly as written

The Status box at the bottom shows how many characters have been used this month, and holds the connection test.

Keys in environment variables

An environment variable wins over the setting, so keys can stay out of the database:

ServiceVariable
DeepLKARELIA_TRANSLATE_DEEPL_KEY
GoogleKARELIA_TRANSLATE_GOOGLE_KEY
OpenAI-compatibleKARELIA_TRANSLATE_OPENAI_KEY
AnthropicKARELIA_TRANSLATE_ANTHROPIC_KEY
LibreTranslateKARELIA_TRANSLATE_LIBRETRANSLATE_KEY

Saved translations and cost

Every translation is saved per post, target language, service and version of the post. The next reader who asks for the same post in the same language gets the saved translation at once, and nothing is sent to the service. Editing a post makes a new translation the next time someone asks. After a change of service or model, new requests are translated by the new one. Earlier translations stay saved and are used again if you switch back.

Only new translations count towards the monthly limit and the hourly limit. The cost therefore follows how much your members read across languages, not how much they write.

Language detection

The language of a post is detected on your server when the post is saved, without sending it anywhere. Detection recognises 29 languages: English, Swedish, Danish, Norwegian, German, Dutch, French, Spanish, Italian, Portuguese, Finnish, Polish, Czech, Turkish, Romanian, Hungarian, Indonesian, Russian, Ukrainian, Belarusian, Greek, Arabic, Persian, Hebrew, Hindi, Thai, Chinese, Japanese and Korean.

Code, quotes, web addresses and @mentions are ignored when the language is detected. Posts that are too short to tell get no language; turn on Offer translation when the language is unknown to show the link on them anyway. Translation itself covers every language your service supports.

What is sent where

  • Only the text of the post, and the title of the first post, is sent to the service you chose, and only when a reader asks for a translation.
  • Code blocks, inline code, web addresses, @mentions and your "never translate" words are replaced by placeholders before sending. The visible text of a link is translated; its address is not sent.
  • Translations are stored in your own database, in the table karelialabs_translations, and deleted with their post.
  • Karelia Labs receives nothing: no posts, no keys, no usage data.

When something goes wrong

Readers seeWhat it meansWhat to do
The translation limit for this month has been reachedThe monthly character limit is used upRaise Character limit per month, or wait for the next month
You have translated a lot in the last hourThe reader reached the hourly limitNothing, or raise New translations per reader per hour
The translation service did not answerThe service returned an error or did not answer in timeRun Test the connection in the settings. It shows the service's own message
Your language could not be recognisedThe reader's language could not be worked outCheck Translate into, and that the reader's language pack or browser language is set

Test the connection fails with an authentication error: check the key, and for DeepL that a free key (ending in :fx) is used as is. For an OpenAI-compatible service, check that the address ends in /v1 (or the address the service documents) and that the model name is exact.

A local model returns nothing: turn on Turn off model thinking, and check that the model is loaded in LM Studio or Ollama.

Remove the extension

Disabling the extension keeps its data. To remove the saved translations and the language column as well, run this before you remove the package:

php flarum migrate:reset --extension=karelialabs-translate
composer remove karelialabs/flarum-translate

Settings, including an API key stored in the database, stay in Flarum's settings table until you clear them.

Need help?

Write to support@karelialabs.atlassian.net or use the support portal. Include your Flarum version (php flarum info) and the message from Test the connection, if there is one. Never send your API key.