hocon

Quick Start

Parse a HOCON document, read typed values out of it, and use it for i18n messages.

Parse a HOCON document, read typed values out of it, and use it for i18n messages.

Parse and read

Hocon.parse turns a string of HOCON into an immutable Config. Read values with typed getters; paths are dot-separated.

import io.github.edadma.hocon.*

val config = Hocon.parse("""
  app {
    name = "Roamer"
    window { width = 1024, height = 768 }
    recent-files = ["a.txt", "b.txt"]
    telemetry = false
  }
""")

config.getString("app.name")              // "Roamer"
config.getInt("app.window.width")         // 1024
config.getBoolean("app.telemetry")        // false
config.getStringList("app.recent-files")  // List("a.txt", "b.txt")

// Drill into a sub-tree:
val window = config.getConfig("app.window")
window.getInt("height")                   // 768

A missing path throws MissingPathException; a value of the wrong shape throws WrongTypeException. When a key may be absent, use hasPath or the *Opt variants:

config.hasPath("app.theme")               // false
config.getStringOpt("app.theme")          // None
config.getIntOpt("app.window.width")      // Some(1024)

A note on quoting

HOCON lets you write unquoted strings, but it forbids these characters inside them:

$ " { } [ ] : = , + # ` ^ ? ! @ * & \

Most real UI strings — Hello, world, Are you sure?, {count} items — contain one of them, so quote your translation strings. This is spec-correct HOCON, not a limitation of this library; bare words are fine for identifiers, numbers, and simple paths.

name      = roamer            # fine — a bare identifier
greeting  = "Hello, world"    # must be quoted — contains a comma

i18n messages

HOCON’s nesting and comments make it a comfortable format for translation files. The Messages helper looks a string up by path and fills {name} placeholders:

val en = Hocon.parse("""
  greeting   = "Hello, {name}"
  cart.items = "{count} items in your cart"
""")

val m = Messages(en)
m("greeting", "name" -> "Ada")   // "Hello, Ada"
m("cart.items", "count" -> 3)    // "3 items in your cart"

A placeholder with no matching argument is left untouched, so a missing value is visible rather than silently dropped.

Base locale with overrides

Keep one complete base locale and let each translation override only what it changes. Merge with withFallback:

val base = Hocon.parse("""
  greeting = "Hello"
  farewell = "Goodbye"
  nav { home = "Home", about = "About" }
""")

val fr = Hocon.parse("""
  greeting = "Bonjour"
  nav { home = "Accueil" }
""")

val m = Messages(fr.withFallback(base))
m("greeting")   // "Bonjour"   (translated)
m("farewell")   // "Goodbye"   (from base)
m("nav.home")   // "Accueil"   (translated)
m("nav.about")  // "About"     (from base)

See the merging guide for the full rules, and the Config reference for every getter.

Search

Esc
to navigate to open Esc to close