Где разместить ключевые строки i18n в Java - PullRequest
11 голосов
/ 09 августа 2011

При интернационализации в Java вы назначаете строковый ключ каждому сообщению. Как лучше всего размещать эти строковые ключи? Цель состоит в том, чтобы упростить рефакторинг (например, изменение имени ключа), чистый и читаемый код, разделение проблем, но при этом не дублировать ключи / сообщения, даже если они вызваны из разных частей кода.

//bad way, strings directly in code
messages.getString("hello_key");

-

// better way, use String constants
public static final String HELLO_KEY = "hello_key";
...
messages.getString(HELLO_KEY);

-

// other (better?) way, put all keys in one huge central class
public class AllMessageKeys {
  public static final String HELLO_KEY = "hello_key";
  ...
}

public class Foo {
  ...
  messages.getString(AllMessageKeys.HELLO_KEY);
}

-

// other (better?) way, put all keys in neighbor class
public class FooMessageKeys {
  public static final String HELLO_KEY = "hello_key";
}

public class Foo {
  ...
  messages.getString(FooMessageKeys.HELLO_KEY);
}

Есть еще предложения? Какой лучше? Я нахожусь в Eclipse IDE, если это делает детали рефакторинга более ясными.

Уточнение: в приведенных выше примерах "messages" имеет тип ResourceBundle.

Ответы [ 7 ]

7 голосов
/ 09 августа 2011

Похоже, мы все согласны с тем, что нужна какая-то константа.Когда дело касается констант, я настоятельно предпочитаю Enums.Java Enums очень мощные и явно недооцененные:

String title = Messages.getString(RunDialogMessages.TITLE);

Хорошо, но что мне нужно было сделать, чтобы это выглядело так?Простой интерфейс, перечисление и небольшая модификация стандартной процедуры доступа к сообщениям.Давайте начнем с интерфейса:

public interface MessageKeyProvider {
    String getKey();
}

Перечисление:

public enum RunDialogMessages implements MessageKeyProvider {
    TITLE("RunDialog.Title"),
    PROMPT("RunDialog.Prompt.Label"),
    RUN("RunDialog.Run.Button"),
    CANCEL("RunDialog.Cancel.Button");


    private RunDialogMessages(String key) {
        this.key = key;
    }

    private String key;

    @Override
    public String getKey() {
        return key;
    }
}

И модифицированный getString() метод:

public static String getString(MessageKeyProvider provider) {
    String key = provider.getKey();
    try {
        return RESOURCE_BUNDLE.getString(key);
    } catch (MissingResourceException e) {
        return '!' + key + '!';
    }
}

Просто для завершения картины, давайтемы видим RunDialog.properties (я скоро об этом расскажу):

RunDialog.Title=Run
RunDialog.Prompt.Label=Enter the name of the program to run:
RunDialog.Run.Button=Run
RunDialog.Cancel.Button=Cancel

Очевидно, что вы можете использовать Enum для чтения из файла свойств (встраивая ResourceBundle), однако это, вероятно, нарушит принцип единой ответственности(а также «Не повторяйте себя», так как код доступа должен был бы повторяться).

Возвращаясь к файлу свойств, у меня было ощущение (я могу ошибаться здесь), что одна из ваших целей былачтобы избежать дублирования переводов.Вот почему я поставил два прогона в примере выше.Видите ли, это слово будет переводиться по-разному в зависимости от контекста (что на самом деле довольно распространено).В этом примере, если бы я перевёл это на польский, это выглядело бы так:

RunDialog.Title=Uruchamianie
RunDialog.Prompt.Label=Wpisz nazwę programu do uruchomienia:
RunDialog.Run.Button=Uruchom
RunDialog.Cancel.Button=Anuluj

Это печальная проблема какого-то странного языка с понятием сопряжения ...

2 голосов
/ 09 августа 2011

Я тоже думаю, что первый - худший выбор.В большинстве случаев (ключ используется только одним классом) я бы предпочел второе решение со строковыми константами.

Если на ключ ссылаются несколько классов, лучше использовать соседний класс (используяинтерфейс, подобный упомянутому @moohkooh).

Решение с одним центральным классом создает магнит зависимости, что, на мой взгляд, является плохим дизайном.Лучше было бы использовать соседние интерфейсы с константами для каждого пакета.

Если вы не хотите, чтобы интерфейс содержал константы, вы можете использовать расширенное перечисление:

public enum DESCMessage {

  HELLO("hello_key"),
  OTHER("other_key");

  private final String key;

  private DESCMessage(String key) {
    this.key = key;
  }

  public String key() {
    return key;
  }
}

Это можно использоватькак:

messages.getString(DESCMessage.HELLO.key());
1 голос
/ 14 декабря 2011

Один из хороших способов сделать это объясняется здесь: Короткая статья о новом подходе с использованием NLS с некоторыми преимуществами по сравнению с подходом ResourceBundle.

1 голос
/ 09 августа 2011

Строковые константы - путь.Где вы их определяете, это действительно зависит от вашей структуры кода и использования ключей.Например:

  • Если вы используете ключи только в одном классе, лучше поместить их туда.
  • Если вы повторно используете одни и те же ключи в своем коде, лучше поставитьих в вспомогательном классе (глобальный класс или класс для пакета в зависимости от использования ключа и количества ключей)

С точки зрения рефакторинга, немного сложнее (требуется больше изменений) перемещать константыиз одного класса в другой, чем переименовать их или изменить их значение.

При изменении их значения у вас нет возможности автоматически изменить определенный ресурс.

1 голос
/ 09 августа 2011

Я всегда использую для таких вещей интерфейс, в котором перечислены мои ключи. Название Interace в основном DESC = выпуск / краткое описание / тема и значения ключей. Таким образом, вы можете создать красивый интерфейс и общий интерфейс, например, для ОК или Прервать ключи.

// other (better?) way, put all keys in neighbor class
public interface DESCMessage {
  public static final String HELLO_KEY = "hello_key";
}

public class Foo {
  ...
  messages.getString(DESCMessage.HELLO_KEY);
}
0 голосов
/ 09 августа 2011

IMHO ResourceBundle облегчает использование файлов свойств, определенных для локали. Чтобы использовать ResourceBundle, файлы свойств должны быть названы в соответствии со следующим соглашением: -

BaseName_langCode.properties

OR

BaseName_langCode_countryCode.properties

Вы можете использовать файлы свойств.

0 голосов
/ 09 августа 2011

IMHO Лучшее место для определения этих ключей и связанных с ними строк - это файлы NLS.n И вы должны сохранить их в файлах ResourceBundle

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...