As far as I can tell, localization via .po files is chosen based on HTTP
headers.
Is it also possible to manually choose the language?
I would like to let the user choose their language, instead of forcing
ie. German on everyone in Germany, even through they're a foreigner, or
have different preferences.

Thanks :)

ps. I get this error when using the website frontend:
500 - Internal Server Error

Internal Server Error

Internal error information:
object.Exception@source/vibenews/controller.d(381): Unknown article id!

./vibenews(pure @safe bool std.exception.enforce!(bool).enforce(bool,
lazy const(char)[], immutable(char)[], ulong)+0x6b) [0x7d6fef]
./vibenews(vibenews.controller.GroupRef[immutable(char)[]]
vibenews.controller.Controller.getArticleGroupRefs(vibe.data.bson.BsonObjectID)+0x178)
[0x8c2d7c]
./vibenews(void
vibenews.web.WebInterface.redirectToThreadPost(vibe.http.server.HTTPServerResponse,
immutable(char)[], immutable(char)[], long, vibe.data.bson.BsonObjectID,
vibe.http.status.HTTPStatus)+0x1ec) [0x8e7398]
./vibenews(void
vibenews.web.WebInterface.postArticle(vibe.http.server.HTTPServerRequest, vibe.http.server.HTTPServerResponse)+0xd84)
[0x8e7178]
./vibenews(void
vibe.http.router.URLRouter.handleRequest(vibe.http.server.HTTPServerRequest,
vibe.http.server.HTTPServerResponse).void lambda3!(ulong,
immutable(char)[][]).
lambda3(ulong, scope immutable(char))+0x1dc)
[0x94e888]
./vibenews(void
vibe.http.router.MatchTree!(vibe.http.router.Route).MatchTree.match(immutable(char)[],
scope void delegate(ulong, scope immutable(char)))+0x1dc) [0x94eec8]
./vibenews(void
vibe.http.router.URLRouter.handleRequest(vibe.http.server.HTTPServerRequest,
vibe.http.server.HTTPServerResponse)+0x1a6) [0x94e63a]
./vibenews(bool vibe.http.server.handleRequest(vibe.core.stream.Stream,
vibe.core.net.TCPConnection, vibe.http.server.HTTPServerListener, ref
vibe.http.server.HTTPServerSettings, ref bool)+0x1654) [0x9bb310]
./vibenews(void
vibe.http.server.handleHTTPConnection(vibe.core.net.TCPConnection,
vibe.http.server.HTTPServerListener)+0x17e) [0x9b9bda]
./vibenews(void
vibe.http.server.listenHTTPPlain(vibe.http.server.HTTPServerSettings).doListen(vibe.http.server.HTTPServerSettings,
vibe.http.server.HTTPServerListener,
immutable(char)[]).lambda4(vibe.core.net.TCPConnection)+0x2c) [0x9b98ec]
./vibenews(void vibe.core.drivers.libevent2_tcp.onConnect(int, short,
void*).ClientTask.execute()+0x316) [0x926e3a]
./vibenews(_D4vibe4core4core27
T16makeTaskFuncInfoTDFZvZ16makeTaskFuncInfoFNbDFZvZS4vibe4core4core12TaskFuncInfo12callDelegateFPS4vibe4core4core12TaskFuncInfoZv+0xda)
[0x866abe]
./vibenews(void vibe.core.core.CoreTask.run()+0x173) [0x90e733]
./vibenews(void core.thread.Fiber.run()+0x2a) [0xa4e432]
./vibenews(fiber_entryPoint+0x61) [0xa4e33d]
[(nil)]