Language Files

Discussion in 'Resources' started by gomeow, May 28, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    gomeow

    For people who want to allow their users to change any message and not have their messages hard coded, I have made an enum for easily implementing custom messages.

    Here is the Lang enum, add this enum to your plugin.
    Code:java
    1. package your.package;
    2.  
    3. import org.bukkit.ChatColor;
    4. import org.bukkit.configuration.file.YamlConfiguration;
    5.  
    6. /**
    7. * An enum for requesting strings from the language file.
    8. * @author gomeow
    9. */
    10. public enum Lang {
    11. TITLE("title-name", "&4[&fAWSMPLUGIN&4]:"),
    12. PLAYER_IS_COOL("player-is-cool", "&f%p is cool."),
    13. INVALID_ARGS("invalid-args", "&cInvalid args!"),
    14. PLAYER_ONLY("player-only", "Sorry but that can only be run by a player!"),
    15. MUST_BE_NUMBER("must-be-number", "&cYou need to specify a number, not a word."),
    16. NO_PERMS("no-permissions", "&cYou don''t have permission for that!");
    17.  
    18. private String path;
    19. private String def;
    20. private static YamlConfiguration LANG;
    21.  
    22. /**
    23.   * Lang enum constructor.
    24.   * @param path The string path.
    25.   * @param start The default string.
    26.   */
    27. Lang(String path, String start) {
    28. this.path = path;
    29. this.def = start;
    30. }
    31.  
    32. /**
    33.   * Set the {@code YamlConfiguration} to use.
    34.   * @param config The config to set.
    35.   */
    36. public static void setFile(YamlConfiguration config) {
    37. LANG = config;
    38. }
    39.  
    40. @Override
    41. public String toString() {
    42. if (this == TITLE)
    43. return ChatColor.translateAlternateColorCodes('&', LANG.getString(this.path, def)) + " ";
    44. return ChatColor.translateAlternateColorCodes('&', LANG.getString(this.path, def));
    45. }
    46.  
    47. /**
    48.   * Get the default value of the path.
    49.   * @return The default value of the path.
    50.   */
    51. public String getDefault() {
    52. return this.def;
    53. }
    54.  
    55. /**
    56.   * Get the path to the string.
    57.   * @return The path to the string.
    58.   */
    59. public String getPath() {
    60. return this.path;
    61. }
    62. }
    63.  

    For the actual enum values, the first parameter is the path to it in the yaml config, and the second is the default value.

    First, add these fields to your main class:
    Code:java
    1. public static YamlConfiguration LANG;
    2. public static File LANG_FILE;


    Next, you must call this method in your onEnable:
    Code:java
    1. /**
    2.  * Load the lang.yml file.
    3.  * @return The lang.yml config.
    4.  */
    5. public void loadLang() {
    6. File lang = new File(getDataFolder(), "lang.yml");
    7. if (!lang.exists()) {
    8. try {
    9. getDataFolder().mkdir();
    10. lang.createNewFile();
    11. InputStream defConfigStream = this.getResource("lang.yml");
    12. if (defConfigStream != null) {
    13. YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
    14. defConfig.save(lang);
    15. Lang.setFile(defConfig);
    16. return defConfig;
    17. }
    18. } catch(IOException e) {
    19. e.printStackTrace(); // So they notice
    20. log.severe("[PluginName] Couldn't create language file.");
    21. log.severe("[PluginName] This is a fatal error. Now disabling");
    22. this.setEnabled(false); // Without it loaded, we can't send them messages
    23. }
    24. }
    25. YamlConfiguration conf = YamlConfiguration.loadConfiguration(lang);
    26. for(Lang item:Lang.values()) {
    27. if (conf.getString(item.getPath()) == null) {
    28. conf.set(item.getPath(), item.getDefault());
    29. }
    30. }
    31. Lang.setFile(conf);
    32. MainClass.LANG = conf;
    33. MainClass.LANG_FILE = lang;
    34. try {
    35. conf.save(getLangFile());
    36. } catch(IOException e) {
    37. log.log(Level.WARNING, "PluginName: Failed to save lang.yml.");
    38. log.log(Level.WARNING, "PluginName: Report this stack trace to <your name>.");
    39. e.printStackTrace();
    40. }
    41. }

    This will load the lang.yml, by copying the default one from the jar, then it will set the yaml file in the Lang enum. This will allow that enum to use the lang.yml to get the user defined messages.

    Also, add these methods to your main class:
    Code:java
    1. /**
    2. * Gets the lang.yml config.
    3. * @return The lang.yml config.
    4. */
    5. public YamlConfiguration getLang() {
    6. return LANG;
    7. }
    8.  
    9. /**
    10. * Get the lang.yml file.
    11. * @return The lang.yml file.
    12. */
    13. public File getLangFile() {
    14. return LANG_FILE;
    15. }


    Next, make a file next to your config.yml like this:
    Code:
    # Use & for color codes.
    # %p is where the player name will get inserted.
    title-name: "&4[&PluginName&4]:"
    player-is-cool: "&f%p is cool."
    player-only: "Sorry but that can only be run by a player!"
    must-be-number: "&cYou need to specify a number, not a word."
    invalid-args: "&cInvalid args!"
    no-permissions: "&cYou don't have permission for that!"
    Lastly, to use it say, if a player does not have permission to do something, we would use it like this:
    Code:
    player.sendMessage(Lang.TITLE.toString() + Lang.NO_PERMS);
    Now if you have messages that vary depending on some variable, say you wanted to say some player is cool, you would use it like this:
    Code:
    Bukkit.broadcastMessage(Lang.TITLE.toString() + Lang.PLAYER_IS_COOL.toString().replace("%p", player.getName()));
    I hope it helps you :D

    As seen in action: https://github.com/drtshock/PlayerV...java/com/drtshock/playervaults/util/Lang.java

    drtshock helped sorta kinda
     
  2. Offline

    drtshock Retired Staff

    Awesome. People should definitely use this for their plugins instead of hard coding messages. Makes things so much easier :)
     
  3. Offline

    ELCHILEN0

    Doing this makes localization much simpler and I recommend everyone to use it or adopt their own way of externalizing strings! Eclipse actually has this built in under Source > Externalize Strings.
     
    drtshock likes this.
  4. Offline

    TheE

    You might want to force UTF-8 encoding for the language file to allow all special characters from the individual languages that are also usable in minecraft.
     
  5. Offline

    xxyy98

    TheE How would one force UTF-8 encoding? I've had this problem with my language files for a long time. (Have I missed a constructor of YamlConfiguration ?)
     
  6. Offline

    TheE

    Unfortunately you cannot use bukkit's configuration API here, because it always uses the encoding of the operation system it runs on. I have no idea why anybody could think (and still thinks!) this would be a great idea, but anyway...

    You will need to write your own YamlConfiguration implementation using the snackyaml-library bundled with bukkit and implement encoding support, or just force UTF-8 completely. As far as I remember, sk89q has done exactly that for worldedit, so you could just use his code if your project is licensed under GPL.
    If you do not need or want yaml, you just need to make sure that you set the encoding correctly when reading the files (InputStreamReader can be initialized with the file's encoding). You may have a look at the LanguageManager I wrote for MyWarp, although I strongly want to move to Yaml soon.

    Something that has not been stressed yet is that java actually has its own standards for localisations, RessourceBundles. I have not really looked into them until now, but I recently discovered that aumgn has written a yaml-version for BukkitUtils, so this might be worth a look too.

    Edit: According to Wolvereness they have implemented encoding somehow, but I have no idea how and where. There is nothing to be found in the docs of file-configuration and yaml-configuration.
     
    xxyy98 likes this.
  7. Offline

    ImDeJay

    gomeow

    I implemented this into my plugin the other day, and after doing some testing with it today, I realised there is a problem, idk if it is just me or what.

    everything works fine upon the first load.
    but when I shut the server down and restart it, everything in my config file is loaded without quotations

    Example, I save like this
    Code:
    NoPermission: "Sorry, you don't have permission to run this command."
    but after restarting the server it loads like this
    Code:
    NoPermission: Sorry, you don't have permission to run this command.
    this is causing problems with some of my wordings that use apostrophe... is this an error on me or with this code?
     
  8. Offline

    bobacadodl

    sheigutn likes this.
  9. Offline

    ImDeJay

    Can anybody answer my question above, perhaps someone has had this problem before?
     
  10. Offline

    gomeow

    ImDeJay
    I'll need to look into it
     
  11. Offline

    wiedzmin137

    Hello. Is there option to make .toString().replace("%points" getPlayerPoints(hero))) acceptable for int data type (getPlayerPoints(hero) returns int, not string, I mustn't change that)?
     
  12. Offline

    DarkBladee12

    wiedzmin137 You can simply make an integer to a string with Integer.toString(INTEGER);
     
    wiedzmin137 likes this.
  13. Offline

    gomeow

    wiedzmin137 likes this.
  14. Offline

    Panjab

    I'd recommend a *.xml-File for Language Files.
    In my opinion it's more efficient and you've got more possibilities with it. Might be different to every plugin, but as soon as your plugin gets bigger it would be the best way to use *.xml.

    You can figure it out with some methodes in which you use the APIs JavaX and W3C.Dom. :)
    My LanguageHandler contains 2 important methods:

    getRawMessage(String language, String element, String messageKey)
    getColoredMessage(String langugae, String messageKey)

    The first method does search for the language file in a specified format - like "lang_<shortcut>.xml".
    The element means which XML-element you'll use.

    Code:
    <language>
        <info key="name" value="English">
        <info key="shortcut" value="en_US">
        ...
     
        <messages>
          <message key="noperm" value="Insufficient permissions!" />
          ..
        </messages>
    </language>
    So, if you want to get the shortcut for example, you have to use the element "info" with the messageKey "shortcut". ;)

    The second method simply colors the raw message, for example in my plugin:

    Code:
        public String getColoredMessage(String lang, String messageKey) {
            return ChatColor.translateAlternateColorCodes('@', getRawMessage(lang, "message", messageKey));
        }
    That's the usage of XML.

    gz

    PS: Sorry for my English, not my main language :)
     
    L33m4n123 likes this.
  15. Offline

    Snicktrix

    When you say create a file next to your Config.yml, what do we name it? And how exactly do we create it? I'm sorry for the beginner question, I'm new to using configs :3
     
  16. Offline

    gidonyou

    I copy and paste everything written there, but this shows error..


    Code:java
    1. return defConfig;

    Void methods cannot return a value

    Did I missed Anything?[/quote]

    Plus how to add other Language?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 1, 2016
  17. Offline

    gomeow

    Name it whatever you want, just make sure it is the same in the code. (The example uses lang.yml). Create the same way you create the plugin.yml

    Sounds like you need to check the method declaration.

    You edit the Lang file. Or you can supply your users with multiple files, and they can switch them out
     
  18. Offline

    gidonyou

    What Method should I use so they can switch the file? (by config)
     
  19. Offline

    gomeow

    The user renames the file
     
  20. Offline

    SoThatsIt

    wow, this is awesome, thanks gomeow! will definitely be using this in my new plugins
     
    gomeow likes this.
  21. Offline

    mickedplay

    gomeow Your loadLang-methode cannot return defConfig; (l. 16)
     
  22. Offline

    creepers84

    gomeow Fantastic well written tutorial, awesome functionality, well done :)
     
    gomeow likes this.
  23. Offline

    Flybelette

Thread Status:
Not open for further replies.

Share This Page