Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controlling the locale: documentation + examples #1339

Merged
merged 2 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions docs/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9573,6 +9573,134 @@ Options with a <<Default Values,default value>> can use the `${DEFAULT-VALUE}` v
userName=Specify the user name. The default is ${DEFAULT-VALUE}.
----

=== Controlling the locale

In your localized application, it may be desirable to specify the locale in order to determine the language of message texts and help output.
One way of controlling the locale is to give `-Duser.language=desiredLocale` as VM argument when running the app.
A more accessible and user-friendly approach is to to implement a command line parameter (e. g. `--locale`) inside your app which can be used to change the locale.
The latter technique requires a two-phase approach to parsing in your application in order to get a valid load order.
The minimal example below demonstrates how to implement this two phase approach:

.Java
[source,java,role="primary"]
----
class InitLocale {
@Option(names = { "-l", "--locale" }, description = "locale for message texts (phase 1)")
void setLocale(String locale) {
Locale.setDefault(new Locale(locale));
}

@Unmatched
List<String> remainder; // ignore any other parameters and options in the first parsing phase
}

@Command(name = "GreetingApp", resourceBundle = "mybundle", mixinStandardHelpOptions = true)
public class GreetingApp implements Runnable {
@Option(names = { "-l", "--locale" }, paramLabel = "<locale>")
private String ignored;

@Parameters(arity = "1..", paramLabel = "<name1> <name2>")
private String[] names;

ResourceBundle bundle = ResourceBundle.getBundle("mybundle");

public void run() { // business logic here
for (String name : names) {
System.out.println(MessageFormat.format(bundle.getString("Hello"), name));
}
}

public static void main(String[] args) {
// first phase: configure locale
new CommandLine(new InitLocale()).parseArgs(args);

// second phase: parse all args (ignoring --locale) and run the app
new CommandLine(new GreetingApp()).execute(args);
}
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
class InitLocale {
@Option(names = ["-l", "--locale"], description = ["locale for message texts (phase 1)"])
fun setLocale(locale: String?) {
Locale.setDefault(Locale(locale))
}

@Unmatched
lateinit var others : List<String> // ignore other parameters/options in first parsing phase
}

@Command(name = "GreetingApp", resourceBundle = "mybundle", mixinStandardHelpOptions = true)
class GreetingApp : Runnable {
@Option(names = ["-l", "--locale"], paramLabel = "<locale>")
lateinit var ignored: String

@Parameters(arity = "1..", paramLabel = "<name1> <name2>")
lateinit var names: Array<String>

private var bundle = ResourceBundle.getBundle("mybundle")

override fun run() { // business logic here
names.onEach {
println(MessageFormat.format(bundle.getString("Hello"), it))
}
}
}

fun main(args: Array<String>) {
// first phase: configure locale
CommandLine(picocli.examples.kotlin.i18n.localecontrol.InitLocale()).parseArgs(*args)

// second phase: parse all args (ignoring --locale) and run the app
exitProcess(CommandLine(GreetingApp()).execute(*args))
}
----

Now put the default properties file (`mybundle.properties`, in English) and the Spanish language variant (`mybundle_es.properties`) in place:

.mybundle.properties
----
Hello = Hello {0}!
----

.mybundle_es.properties
----
Hello = ¡Hola {0}!
----

Eventually, we are ready to run our application:

[source,bash]
----
java -cp "picocli-4.6.2-SNAPSHOT.jar;myapp.jar" org.myorg.GreetingApp Sarah Lea
----

With no command line parameter `--locale` given, the message texts are printed in the default language (here: English):

----
Hello Sarah!
Hello Lea!
----

In order to control the locale choosen for our output, we have to make use of the command line parameter `--locale`:

[source,bash]
----
java -cp "picocli-4.6.2-SNAPSHOT.jar;myapp.jar" org.myorg.GreetingApp --locale=es Sarah Lea
----

Now our message texts are printed in Spanish:

----
¡Hola Sarah!
¡Hola Lea!
----

Using the command line parameter `--locale`, one can also determine the language of the help output of your application.
The https://github.com/remkop/picocli/blob/master/picocli-examples[`picocli-examples`] module has examples with fully implemented, localized help output, coded both in https://github.com/remkop/picocli/blob/master/picocli-examples/src/main/java/picocli/examples/i18n/localecontrol/LocaleControl.java[Java] and https://github.com/remkop/picocli/tree/master/picocli-examples/src/main/kotlin/picocli/examples/kotlin/i18n/localecontrol/LocaleControl.kt[Kotlin].

== Variable Interpolation

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package picocli.examples.i18n.localecontrol;

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.Unmatched;

import java.io.File;
import java.math.BigInteger;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;

class InitLocale {
@Option(names = { "-l", "--locale" }, description = "locale used for message texts (phase 1)")
void setLocale(String locale) {
Locale.setDefault(new Locale(locale));
}

@Unmatched
List<String> remainder; // ignore any other parameters and options in the first parsing phase
}

@Command(name = "checksum", mixinStandardHelpOptions = true, version = "checksum 4.0",
resourceBundle = "picocli.examples.i18n.localecontrol.bundle",
sortOptions = false)
public class LocaleControl implements Callable {

ResourceBundle bundle = ResourceBundle.getBundle("picocli.examples.i18n.localecontrol.bundle");

@Option(names = { "-l", "--locale" }, descriptionKey = "Locale", paramLabel = "<locale>", order = 1)
private String ignored;

@Parameters(index = "0", descriptionKey = "File")
private File file;

@Option(names = {"-a", "--algorithm"}, descriptionKey = "Algorithms", order = 2)
private String algorithm = "MD5";

@Override
public Integer call() throws Exception {
byte[] fileContents = Files.readAllBytes(file.toPath());
byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents);
System.out.printf("%s: %s%n", bundle.getString("Label_File"), file);
System.out.printf("%s: %s%n", bundle.getString("Label_Algorithm"), algorithm);
System.out.printf("%s: %0" + (digest.length*2) + "x", bundle.getString("Label_Checksum"), new BigInteger(1, digest));
return 0;
}

public static void main(String... args) {
// first phase: configure locale
new CommandLine(new InitLocale()).parseArgs(args);

// second phase: parse all args (ignoring --locale) and run the app
int exitCode = new CommandLine(new LocaleControl()).execute(args);
System.exit(exitCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Labels
Label_File = File
Label_Algorithm = Algorithm
Label_Checksum = Checksum

# Header
usage.headerHeading = Demo: Controlling the locale of message texts%n
usage.header = This application prints the checksum (MD5 by default)
usage.header.0 = of a given file to STDOUT.%n

# Description
usage.descriptionHeading = %nSynopsis:%n
usage.description = This demo app exemplifies how to control the output
usage.description.0 = of localised messages text from a picocli based command
usage.description.1 = line application. Depending on the specified command
usage.description.2 = line option '--locale', either German or English message
usage.description.3 = texts will be printed out. Specify '--locale=de' as option
usage.description.4 = in order to enforce output of German message texts.%n

# Parameters
usage.parameterListHeading=Parameters:%n

# Options
usage.optionListHeading=Options:%n
Locale = Locale for message texts
File = The file whose checksum to calculate.
Algorithms = MD5, SHA-1, SHA-256, ...

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Labels
Label_File = Datei
Label_Algorithm = Algorithmus
Label_Checksum = Pr�fsumme

# Header
usage.headerHeading = Demo: Kontrolle der Lokale von Meldungstexten%n
usage.header = Diese Anwendung gibt die Pr�fsumme (Voreinstellung: MD5)
usage.header.0 = einer Datei auf STDOUT aus.%n

# Description
usage.descriptionHeading = %nSynopsis:%n
usage.description = Diese Demo-Anwendung veranschaulicht die Kontrolle der Ausgabe
usage.description.0 = von lokalisierten Meldungstexten innerhalb eines picocli-basierten
usage.description.1 = Kommandozeilenprogramms. In Abh�ngigkeit von der spezifizierten
usage.description.2 = Kommandozeilenoption '--locale' k�nnen deutsche oder englische Texte
usage.description.3 = ausgegeben werden. Geben Sie '-locale=en' als Kommandozeilenoption
usage.description.4 = an, um die Ausgabe von englischen Meldungstexten zu erzwingen.%n

# Synopsis
usage.synopsisHeading=Aufruf:\u0020

# Parameters
usage.parameterListHeading=Parameter:%n

# Options
usage.optionListHeading=Optionen:%n
File = Die Datei, deren Pr�fsumme berechnet wird.
Locale = Locale f�r Meldungstexte ('de' oder 'en')

# Standard help mixin options:
help = Zeige Hilfemeldung f�r die Anwendung
version = Zeige Versionsinformation f�r die Anwendung
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package picocli.examples.kotlin.i18n.localecontrol

import picocli.CommandLine
import picocli.CommandLine.Command
import picocli.CommandLine.Option
import picocli.CommandLine.Parameters
import picocli.CommandLine.Unmatched

import java.io.File
import java.lang.Exception
import java.math.BigInteger
import java.nio.file.Files
import java.security.MessageDigest
import java.util.*
import java.util.concurrent.Callable
import kotlin.system.exitProcess

class InitLocale {
@Option(names = ["-l", "--locale"], description = ["locale used for message texts (phase 1)"])
fun setLocale(locale: String?) {
Locale.setDefault(Locale(locale))
}

// ignore any other parameters and options in the first parsing phase
@Unmatched
var remainder : List<String>? = null
}

@Command(name = "checksum", mixinStandardHelpOptions = true, version = ["checksum 4.0"],
resourceBundle = "picocli.examples.i18n.localecontrol.bundle", sortOptions = false)
class LocaleControl : Callable<Int> {
private var bundle: ResourceBundle = ResourceBundle.getBundle("picocli.examples.kotlin.i18n.localecontrol.bundle")

@Option(names = ["-l", "--locale"], descriptionKey = "Locale", paramLabel = "<locale>", order = 1)
lateinit var ignored: String

@Parameters(index = "0", descriptionKey = "File")
lateinit var file: File

@Option(names = ["-a", "--algorithm"], descriptionKey = "Algorithms", order = 2)
var algorithm = "MD5"

@Throws(Exception::class)
override fun call(): Int {
val fileContents = Files.readAllBytes(file.toPath())
val digest = MessageDigest.getInstance(algorithm).digest(fileContents)
println("%s: %s".format(bundle.getString("Label_File"), file))
println("%s: %s".format(bundle.getString("Label_Algorithm"), algorithm))
println(("%s: %0" + digest.size * 2 + "x").format(bundle.getString("Label_Checksum"), BigInteger(1, digest)))
return 0
}
}

fun main(args: Array<String>) {
// first phase: configure locale
CommandLine(InitLocale()).parseArgs(*args)

// second phase: parse all args (ignoring --locale) and run the app
exitProcess(CommandLine(LocaleControl()).execute(*args))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Labels
Label_File = File
Label_Algorithm = Algorithm
Label_Checksum = Checksum

# Header
usage.headerHeading = Demo: Controlling the locale of message texts%n
usage.header = This application prints the checksum (MD5 by default)
usage.header.0 = of a given file to STDOUT.%n

# Description
usage.descriptionHeading = %nSynopsis:%n
usage.description = This demo app exemplifies how to control the output
usage.description.0 = of localised messages text from a picocli based command
usage.description.1 = line application. Depending on the specified command
usage.description.2 = line option '--locale', either German or English message
usage.description.3 = texts will be printed out. Specify '--locale=de' as option
usage.description.4 = in order to enforce output of German message texts.%n

# Parameters
usage.parameterListHeading=Parameters:%n

# Options
usage.optionListHeading=Options:%n
Locale = Locale for message texts
File = The file whose checksum to calculate.
Algorithms = MD5, SHA-1, SHA-256, ...

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Labels
Label_File = Datei
Label_Algorithm = Algorithmus
Label_Checksum = Pr�fsumme

# Header
usage.headerHeading = Demo: Kontrolle der Lokale von Meldungstexten%n
usage.header = Diese Anwendung gibt die Pr�fsumme (Voreinstellung: MD5)
usage.header.0 = einer Datei auf STDOUT aus.%n

# Description
usage.descriptionHeading = %nSynopsis:%n
usage.description = Diese Demo-Anwendung veranschaulicht die Kontrolle der Ausgabe
usage.description.0 = von lokalisierten Meldungstexten innerhalb eines picocli-basierten
usage.description.1 = Kommandozeilenprogramms. In Abh�ngigkeit von der spezifizierten
usage.description.2 = Kommandozeilenoption '--locale' k�nnen deutsche oder englische Texte
usage.description.3 = ausgegeben werden. Geben Sie '-locale=en' als Kommandozeilenoption
usage.description.4 = an, um die Ausgabe von englischen Meldungstexten zu erzwingen.%n

# Synopsis
usage.synopsisHeading=Aufruf:\u0020

# Parameters
usage.parameterListHeading=Parameter:%n

# Options
usage.optionListHeading=Optionen:%n
File = Die Datei, deren Pr�fsumme berechnet wird.
Locale = Locale f�r Meldungstexte ('de' oder 'en')

# Standard help mixin options:
help = Zeige Hilfemeldung f�r die Anwendung
version = Zeige Versionsinformation f�r die Anwendung