Localization

Loreline has built-in support for translating your scripts into multiple languages.

How it works

Localization in Loreline follows three steps:

  1. Tag translatable text with #key hash comments in your script.
  2. Create a translation file (.lang.lor) for each target language.
  3. Load the translation file at runtime and pass it to the interpreter.

The interpreter looks up each #key at runtime and replaces the original text with the translated version. If a key has no translation, the original text is used as a fallback.

Tagging text for translation

You don't have to add these keys by hand. The CLI can generate them automatically. See Using the CLI below.

To mark text for translation, add a #key hash comment after any translatable element. Three types of content can be tagged:

Text statements (narrative):

"The cafe smells wonderful." #intro

Dialogue (character speech):

barista: "Welcome! What can I get you?" #welcome

Choice options:

choice
  Espresso #opt-espresso
  Latte #opt-latte

Keys can be any combination of letters, digits, hyphens, and underscores, for example #intro, #opt-espresso, or #chapter2_greeting.

Literal hash characters

If you need a literal # in your text, use ## (doubled) or \# (escaped):

This has a literal ##hashtag in text.
barista: "Use \#escaped for literal hash."

Full example

Here's a complete script with localization keys:

character barista
  name: Alex

beat Menu
  "The cafe smells wonderful." #intro
  barista: "Welcome! What can I get you?" #welcome
  choice
    Espresso #opt-espresso
      barista: One espresso! #espresso-reply
    Latte #opt-latte
      barista: One latte! #latte-reply

Worried about readability? In the VS Code extension, press Cmd+Shift+H (Mac) or Ctrl+Shift+H (Windows/Linux), or use the command palette, to hide all localization keys so they never get in the way of reading or writing your script. The Playground on this website also has this option.

Translation files

Translation files use the naming convention Filename.lang.lor, for example CoffeeShop.fr.lor for French or CoffeeShop.de.lor for German.

Each entry in a translation file consists of:

Here's the French translation for the example above (CoffeeShop.fr.lor):

#intro // "The cafe smells wonderful."
Le café sent très bon.

#welcome // "Welcome! What can I get you?"
Bienvenue ! Qu'est-ce que je te sers ?

#opt-espresso // Espresso
Expresso

#espresso-reply // One espresso!
Un expresso !

#opt-latte // Latte
Latté

#latte-reply // One latte!
Un latté

The // comment on each key line is there for reference only. The interpreter ignores it. Translators can see the original text without needing to open the source file.

Using the CLI

The loreline translate command automates the localization workflow. It can generate keys, create translation files, and clean up.

Auto-generate keys

If your script doesn't have #key tags yet, the CLI can add them automatically:

loreline translate CoffeeShop.lor --auto-ids

This scans all text, dialogue, and choice nodes in the script and inserts random keys (like #a7k2m) on any line that doesn't already have one. Your existing comments and formatting are preserved.

Generate a translation file

To create or update a translation file for a specific language:

loreline translate CoffeeShop.lor --lang fr

This creates CoffeeShop.fr.lor with all translatable entries. If the file already exists, existing translations are preserved and new entries are added.

Remove keys

To strip all #key hash comments from a source file:

loreline translate CoffeeShop.lor --clear

Typical workflow

# 1. Add keys to your script
loreline translate CoffeeShop.lor --auto-ids

# 2. Generate a French translation file
loreline translate CoffeeShop.lor --lang fr

# 3. Edit CoffeeShop.fr.lor, replace each placeholder with the French text

# 4. Add more languages as needed
loreline translate CoffeeShop.lor --lang de
loreline translate CoffeeShop.lor --lang es

When you add new text to your script, run --auto-ids again to tag the new lines, then --lang fr to update the translation file. Existing translations won't be overwritten.

Interpolation in translations

Translations work seamlessly with text interpolation. Variables like $count or ${expression} are preserved in the translation file and evaluated at runtime:

Source script:

state
  count: 3

beat Start
  You have $count items #item-count
  barista: You ordered $count drinks #drink-reply

Translation file (Story.fr.lor):

#item-count // You have $count items
Vous avez $count articles

#drink-reply // You ordered $count drinks
Vous avez commandé $count boissons

At runtime, $count is replaced with its current value (3), producing "Vous avez 3 articles".

Loading translations at runtime

To use translations in your application, parse the translation file and pass the extracted translations to the interpreter. Here's a JavaScript example:

import { Loreline } from 'loreline';

// Parse the main script
const script = Loreline.parse(sourceContent);

// Parse the translation file
const translationScript = Loreline.parse(frenchContent);
const translations = Loreline.extractTranslations(translationScript);

// Play with French translations
Loreline.play(script, onDialogue, onChoice, onFinish, {
  translations: translations
});

The same pattern applies in all supported languages. See the integration guides for details on C#, C++, Python, Lua, and Haxe.