Форум программистов, компьютерный форум, киберфорум
Java
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.85/27: Рейтинг темы: голосов - 27, средняя оценка - 4.85
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225

Шаблоны проектирования для смены языка программы.

08.02.2011, 10:43. Показов 5241. Ответов 22
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Требуется создать библиотеку для смены языка пользовательского интерфейса программ.
В принципе, решается это логко - берется интерфейс Observer и класс Observable и на их основе делается смена языка. Только вот слишком уж геморройно для каждого визуального компонента создавать наследника, реализующего интерфейс Observer. Хотя это наиболее гибкое и универсальное решение.
Можно пойти по другому пути, и вместо реализации интерфейса сделать отдельный класс, который по типу изменяемого обьекта (и, возможно, передаваемым параметрам) будет определять, что именно в обьекте изменить. Однако такой подход требует серьезного обдумывания, поскольку библиотека должна быть стандартной.
Кто сталкивался с проблемой изменения языка программы и имеет готовое и удобное решение?
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
08.02.2011, 10:43
Ответы с готовыми решениями:

Шаблоны проектирования
Мне нужно разработать в Java консольное приложение, которое считывает из файла (или файлов, по решению разработчика) список платежей...

Шаблоны проектирования
Задача Разработать программу, языком программирования JAVA, моделирующий систему указанную в варианте задачи, с использованием шаблонов...

Шаблоны проектирования (литература для изучения)
подскажите литературу для изучения . спасибо.

22
paradise
08.02.2011, 11:07
Как насчет Localization и Internationalization?
3 / 3 / 0
Регистрация: 25.08.2010
Сообщений: 213
08.02.2011, 16:32
ResourceBundle + все в файлы вынести.
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
09.02.2011, 09:54  [ТС]
А если язык надо менять в процессе работы программы, когда все визуальные компоненты уже созданы?
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
09.02.2011, 12:52
Если стоит требование смены языка во время работы программы, придется работать с look & feel. А именно, есть два пути:
1. Написать look & feel warpper, который будет почти все операции делегировать тому look & feel, который в нем завернут, но переопределит методы, отвечающие за прорисовку, в UI всех компонентов, где возможно появление текста. В переопределенных методах использовать заданный пользователем текст в качестве ключа, а отображать то, что будет по этому ключу вынуто из ResourceBundle. В таком случае можно будет использовать произвольный look & feel и при этом останется возможность 'перелокализации' на лету.
2. Написать subclass какого-нибудь look & feel, опять же переопределив методы, рисующие компонент.

Первый подход мне нравится больше, но он требует некоторой дополнительной проработки, поэтому приведу пример использования второго подхода. В BasicLabelUI.java имеем:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    /**
     * Paint clippedText at textX, textY with the labels foreground color.
     * 
     * @see #paint
     * @see #paintDisabledText
     */
    protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY)
    {
        int mnemIndex = l.getDisplayedMnemonicIndex();
        g.setColor(l.getForeground());
        BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex,
                                                     textX, textY);
    }
 
    /**
     * Paint clippedText at textX, textY with background.lighter() and then 
     * shifted down and to the right by one pixel with background.darker().
     * 
     * @see #paint
     * @see #paintEnabledText
     */
    protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY)
    {
        int accChar = l.getDisplayedMnemonicIndex();
        Color background = l.getBackground();
        g.setColor(background.brighter());
        BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, accChar,
                                                   textX + 1, textY + 1);
        g.setColor(background.darker());
        BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, accChar,
                                                   textX, textY);
    }
Ваш labelUI (наследник, например, MetalLabelUI и через него BasicLabelUI) мог бы содержать следующий код:
Java
1
2
3
4
5
6
7
8
    protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY)
    {
        int mnemIndex = l.getDisplayedMnemonicIndex();
        g.setColor(l.getForeground());
        String realText = ResourceBundle.getResourceBundle(MyTags.currentLanguageCode).getString(s);
        BasicGraphicsUtils.drawStringUnderlineCharAt(g, realText, mnemIndex,
                                                     textX, textY);
    }
При использовании такого подхода для смены языка нужно изменить MyTags.currentLaguageCode и проделать стандартные действия, как при смене look & feel.
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
09.02.2011, 15:52  [ТС]
Проще написать для каждой компоненты с текстом потомка, реализующего интерфейс Languageable (или что-то типа того)
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
09.02.2011, 18:51
А еще проще - вообще ничего не менять На самом деле в каких-то случаях действительно такой подход с натяжкой сработает, но сложности возникнут в следующих ситуациях:
1. Стандартная библиотека содержит композитные компоненты, типа FileChooser или диалогов, которые показывает JOptionPane. В них во всех уже используются JButton. Конечно, там все без проблем кастомизируется, но эту кастомизацию нужно будет каждый раз кодировать, т.к. localization framework, построенный на наследовании, таких случаев не покрывает.
2. Заголовки JTable. Опять же можно все лопатить ручками, но геморно и ясность кода страдает.
3. Любые библиотечные компоненты, начиная с какого-нибудь календарика, не будут работать с локализацией на базе subclassing. Наверное, вы там пользуетесь библиотеками?

Ну и наконец, если вам просто написать подклассы для нескольких визуальных компонентов, то почему вместо этого не написать такое же количество подклассов для UI тех же самых компонентов?
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
10.02.2011, 10:12  [ТС]
Логично
Однако и тут гемора достаточно. Как я понял, работать это будет следующим образом - компоненте в качестве текста дается определенный ключ, а ее UI по этому ключу извлекает нужный текст из базы данных и прорисовывает его. Если значения, соответствующего данному ключу, в базе нет, значит ключ и есть текст (чтобы не забивать базу данными, которые в любом используемом программой языке выглядят одинаково - например, цифры).
Однако остаются две проблемы. Первая - как после изменения языка перерисовать все видимые компонеты и вторая - скорость прорисовки. Если изменять компонеты, скорость прорисовки остается прежней, поскольку каждый компонет знает свой текст, который при изменении языка меняется всего один раз. Если же изенять UI компонет, то текст прийдется извлекать из базы данных при каждой прорисовке.
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
10.02.2011, 15:18
Первая проблема - перерисовать весь GUI:
Code
1
  SwingUtilities.updateComponentTreeUI(myFrame);
Вторая проблема - обращения к БД, может быть решена, например, такими способами:
1. Каждый UI содержит map, где хранит все переводы, уже выбиравшиеся из БД Эти map сбрасываются тем же методом, который меняет язык GUI.
Для текстов, не имеющих переводов, в map в качестве перевода кладется тот же самый текст.
2. Каждый компонент хранит свои значения.
2.1. Делаем класс, который управляет переключением языков. В нем статическая переменная - счетчик переключений языка.
2.2. Класс JComponent имеет методы putClientProperty/getClientProperty. Каждый компонент, который попадает в руки нашего UI, получает три свойства: исходный текст, перевод, текущее значение счетчика переключений языка.
2.3. UI, начиная обрабатывать компонент, проверяет
2.3.1. getClientProperty('text').equals(getText );
2.3.2. getClientProperty('lang_switch_cnt').equ als(LangController.switchCnt);
Если оба условия дают true, значение свойства 'перевод' актуально. В противном случае все свойства переприсваиваются.

Каждый из подходов имеет очевидные сильные/слабые стороны, но оба существенно быстрее, чем выборка из БД. Тут, правда, нужно не забыть, что есть еще tooltips.
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
10.02.2011, 18:06  [ТС]
А если нужно LookAndFeel поменять? ))
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
10.02.2011, 18:21
В моем исходном посте в качестве решения №1 значится 'написать look&feel wrapper'. Это будет работать равно при смене языка и нижележащего look&feel. Правда, усилий потребует немного больше, но не кардинально :0
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
10.02.2011, 19:46  [ТС]
А пару строчек кода можно?
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
13.02.2011, 08:23
Ну, блин... Хорошо, наковыряю на досуге.
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
13.02.2011, 09:06  [ТС]
Все-таки, вариант с LookAndFeel не совсем хорош. Он работает только со стандартными свойствами компонент (setText(), setToolTipText(), setTitle(), setPage() и т.д.). Зачастую же бывает необходимо поменять другой параметр. Например, у нас есть таблица. Заносить в базу данных текст каждой ячейки? Гораздо удобнее передать таблице ссылку на какой-либо файл, пусть она на его основе меняет свое содержимое. Как реализовать это через LookAndFeel?
По-моему, нужно действовать через наследование компонет. Либо комбинировать наследование с LookAndFeel.
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
13.02.2011, 10:54
Пример с таблицей мне не вполне понятен, потому что здесь выбор наиболее удобного варианта зависит от природы источника данных. Но в общем можно сказать, что контент таблицы действительно нужно переводить, оседлав каким-то образом ее модель. Как седлать и как переводить - зависит, как уже говорилось, от обстоятельств.
На самом деле, под термином 'локализация' сосуществуют две отдельные проблемы: локализация GUI и контента. Здесь обсуждалась локализация GUI.
А впрочем, я никого за уши в светлое завтра не тяну )
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
14.02.2011, 09:04  [ТС]
Дмитрий, если не лень, подключитесь ко мне по ICQ - 82347216 мой номер.
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
15.02.2011, 08:11
Обещанная пара строчек кода. Это Look&Feel wrapper, который локализует JLabels. Каждый файл буду отправлять отдельным постом.
Просьба не расчитывать на то, что в присланном материале нет багов (по крайней мере один точно есть, с определением preferred size).
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
15.02.2011, 08:12
[code]
package ru.podolsk.pryadm.localization;

import javax.swing.*;
import java.awt.Component;
import java.awt.Frame;
import java.util.*;

/**
*/
public class TranslatingLookAndFeel extends LookAndFeel {
public final static String PROP_LABEL_KEY = 'label_key';
public final static String PROP_LABEL_TEXT = 'label_text';
public final static String PROP_CHANGE_COUNT = 'change_count';

private UIDefaults uiDefaults;

public String getName() {
return underlyingLaF.getName();
}

public String getID() {
return underlyingLaF.getID();
}

public String getDescription() {
return underlyingLaF.getDescription();
}

public boolean isNativeLookAndFeel() {
return underlyingLaF.isNativeLookAndFeel();
}

public boolean isSupportedLookAndFeel() {
return underlyingLaF.isSupportedLookAndFeel();
}

public void initialize() {
uiDefaults = underlyingLaF.getDefaults();
//
String labelUI = TranslatingLabelUI.class.getName();
uiDefaults.put('LabelUI', labelUI);
}

public UIDefaults getDefaults() {
return uiDefaults;
}

public void provideErrorFeedback(Component component) {
underlyingLaF.provideErrorFeedback(compo nent);
}

public boolean getSupportsWindowDecorations() {
return underlyingLaF.getSupportsWindowDecoratio ns();
}

public void uninitialize() {
underlyingLaF.uninitialize();
}

///////////////////////////////////////////////////////////////////////////////////

private static Locale locale;
private static Translator translator;
private static Set frames = new HashSet();
private static LookAndFeel underlyingLaF;
private static Long changeCnt = new Long(0);

static {
ResourceBundleTranslator trans = new ResourceBundleTranslator();
trans.setResourceBundleName('translation s');
locale = Locale.ENGLISH;
trans.setLocale(locale);
setTranslator(trans);
}

public static Long getChangeCount() {
return changeCnt;
}

public static synchronized void addFrame(JFrame frame) {
frames.add(frame);
}

public static synchronized void removeFrame(JFrame frame) {
frames.remove(frame);
}

public static synchronized Translator getTranslator() {
return translator;
}

public static synchronized void setTranslator(Translator argTranslator) {
updateChangeCnt();
translator = argTranslator;
if (locale != null) {
translator.setLocale(locale);
updateUI();
}
}

public static synchronized void setLocale(Locale argLocale) {
updateChangeCnt();
locale = argLocale;
if (translator != null) {
translator.setLocale(locale);
updateUI();
}
}

public static synchronized Locale getLocale() {
return locale;
}

public static synchronized void setUnderlyingLookAndFeel(String className)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
updateChangeCnt();
if (underlyingLaF != null) {
underlyingLaF.uninitialize();
}
underlyingLaF = (LookAndFeel) Class.forName(className).newInstance();
underlyingLaF.initialize();
try {
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
15.02.2011, 08:12
[code]
package ru.podolsk.pryadm.localization;

import javax.accessibility.Accessible;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.LabelUI;
import javax.swing.table.TableCellRenderer;
import java.awt.Dimension;
import java.awt.Graphics;

/**
*/
public class TranslatingLabelUI extends LabelUI {
private LabelUI underlyingUI;

public static ComponentUI createUI(JComponent c) {
TranslatingLabelUI ui = new TranslatingLabelUI();
return ui;
}

public void installUI(JComponent c) {
init(c);
underlyingUI.installUI(c);
}

public void uninstallUI(JComponent c) {
init(c);
underlyingUI.uninstallUI(c);
}

public void paint(Graphics g, JComponent c) {
init(c);
checkLabels(c);
underlyingUI.paint(g, c);
}

public void update(Graphics g, JComponent c) {
init(c);
checkLabels(c);
underlyingUI.update(g, c);
}

public Dimension getPreferredSize(JComponent c) {
init(c);
return underlyingUI.getPreferredSize(c);
}

public Dimension getMinimumSize(JComponent c) {
init(c);
return underlyingUI.getMinimumSize(c);
}

public Dimension getMaximumSize(JComponent c) {
init(c);
return underlyingUI.getMaximumSize(c);
}

public boolean contains(JComponent c, int x, int y) {
init(c);
return underlyingUI.contains(c, x, y);
}

public int getAccessibleChildrenCount(JComponent c) {
init(c);
return underlyingUI.getAccessibleChildrenCount( c);
}

public Accessible getAccessibleChild(JComponent c, int i) {
init(c);
return underlyingUI.getAccessibleChild(c, i);
}

private void init(JComponent c) {
Long changeCountShouldBe = (Long) c.getClientProperty(TranslatingLookAndFe el.PROP_CHANGE_COUNT);
Long changeCountActual = TranslatingLookAndFeel.getChangeCount();
if (changeCountShouldBe == null || ! changeCountActual.equals(changeCountShou ldBe)) {
underlyingUI = (LabelUI) TranslatingLookAndFeel.getUnderlyingLook AndFeel().getDefaults().getUI(c);
}
}

private void checkLabels(JComponent c) {
String key = (String) c.getClientProperty(TranslatingLookAndFe el.PROP_LABEL_KEY);
String labelShouldBe = (String) c.getClientProperty(TranslatingLookAndFe el.PROP_LABEL_TEXT);
String labelActual = ((JLabel)c).getText();
Long changeCountShouldBe = (Long) c.getClientProperty(TranslatingLookAndFe el.PROP_CHANGE_COUNT);
Long changeCountActual = TranslatingLookAndFeel.getChangeCount();
//
if (labelActual != null && !labelActual.equals('') && !(c instanceof TableCellRenderer)) {
if (! labelActual.equals(labelShouldBe)) {
key = labelActual;
labelShouldBe = TranslatingLookAndFeel.getTranslator().t ranslate(key);
labelActual = labelShouldBe;
changeCountShouldBe = changeCountActual;
//
c.putClientProperty(TranslatingLookAndFe el.PROP_LABEL_KEY, key);
c.putClientProperty(TranslatingLookAndFe el.PROP_LABEL_TEXT, labelShouldBe);
c.putClientProperty(TranslatingLookAndFe el.PROP_CHANGE_COUNT, changeCountShouldBe);
((JLabel)c).setText(labelActual);
} else if (! changeCountActual.
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
15.02.2011, 08:13
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package ru.podolsk.pryadm.localization;
 
import java.util.Locale;
 
/**
 */
public interface Translator {
 
    String translate(String text);
 
    void setLocale(Locale locale);
 
    Locale getLocale();
 
    void initialize();
 
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
15.02.2011, 08:13
Помогаю со студенческими работами здесь

Подскажите необходимы паттерны (Шаблоны проектирования) необходимые для решения задачи
Добрый день дорогие знатоки) Выдали экзаменационное задание по паттернам: Спроектируйте систему профессий для RPG игры. Профессия в...

После смены языка программ не поддерживающих юникод на японский некоторые программы стали на японском
после того как я поменял язык программ не поддерживающих юникод на японский некоторые проги и инсталляторы которые были на русском стали на...

Шаблоны проектирования
Шаблоны проектирования, их реализация на С++. Кто знает какие-то хорошие книги, поделитесь :)

Шаблоны проектирования
Разработайте программу для представления структуры объектов подписки на журналы. Один человек может быть подписан на несколько журналов. На...

Шаблоны проектирования
Подскажите пожалуйста. Есть 2 задачи. На шаблон "Абстрактная фабрика" и шаблон "Команда". 1. #include...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Хочу заставить корпорации вкладываться в здоровье сотрудников: делаю мат модель здравосохранения
anaschu 22.03.2026
e7EYtONaj8Y Z4Tv2zpXVVo https:/ / github. com/ shumilovas/ med2. git
1С: Программный отбор элементов справочника по группе
Maks 22.03.2026
Установка программного отбора элементов справочника "Номенклатура" из модуля формы документа. В качестве фильтра для отбора справочника служит группа номенклатуры. Отбор по наименованию группы. . .
Как я обхитрил таблицу Word
Alexander-7 21.03.2026
Когда мигает курсор у внешнего края таблицы, и нам надо перейти на новую строку, а при нажатии Enter создается новый ряд таблицы с ячейками, то мы вместо нервных нажатий Энтеров мы пишем любые буквы. . .
Krabik - рыболовный бот для WoW 3.3.5a
AmbA 21.03.2026
без регистрации и смс. Это не торговля, приложение не содержит рекламы. Выполняет свою непосредственную задачу - автоматизацию рыбалки в WoW - и ничего более. Однако если админы будут против -. . .
1С: Программный отбор элементов справочника по значению перечисления
Maks 21.03.2026
Установка программного отбора элементов справочника "Сотрудники" из модуля формы документа. В качестве фильтра для отбора служит значение перечислений. / / Событие "НачалоВыбора" реквизита на форме. . .
Переходник USB-CAN-GPIO
Eddy_Em 20.03.2026
Достаточно давно на работе возникла необходимость в переходнике CAN-USB с гальваноразвязкой, оный и был разработан. Однако, все меня терзала совесть, что аж 48-ногий МК используется так тупо: просто. . .
Оттенки серого
Argus19 18.03.2026
Оттенки серого Нашёл в интернете 3 прекрасных модуля: Модуль класса открытия диалога открытия/ сохранения файла на Win32 API; Модуль класса быстрого перекодирования цветного изображения в оттенки. . .
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru