3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
1

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

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

Требуется создать библиотеку для смены языка пользовательского интерфейса программ.
В принципе, решается это логко - берется интерфейс Observer и класс Observable и на их основе делается смена языка. Только вот слишком уж геморройно для каждого визуального компонента создавать наследника, реализующего интерфейс Observer. Хотя это наиболее гибкое и универсальное решение.
Можно пойти по другому пути, и вместо реализации интерфейса сделать отдельный класс, который по типу изменяемого обьекта (и, возможно, передаваемым параметрам) будет определять, что именно в обьекте изменить. Однако такой подход требует серьезного обдумывания, поскольку библиотека должна быть стандартной.
Кто сталкивался с проблемой изменения языка программы и имеет готовое и удобное решение?
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
08.02.2011, 10:43
Ответы с готовыми решениями:

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

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

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

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

22
paradise
08.02.2011, 11:07 2
Как насчет Localization и Internationalization?
3 / 3 / 0
Регистрация: 25.08.2010
Сообщений: 213
08.02.2011, 16:32 3
ResourceBundle + все в файлы вынести.
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
09.02.2011, 09:54  [ТС] 4
А если язык надо менять в процессе работы программы, когда все визуальные компоненты уже созданы?
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
09.02.2011, 12:52 5
Если стоит требование смены языка во время работы программы, придется работать с 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  [ТС] 6
Проще написать для каждой компоненты с текстом потомка, реализующего интерфейс Languageable (или что-то типа того)
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
09.02.2011, 18:51 7
А еще проще - вообще ничего не менять На самом деле в каких-то случаях действительно такой подход с натяжкой сработает, но сложности возникнут в следующих ситуациях:
1. Стандартная библиотека содержит композитные компоненты, типа FileChooser или диалогов, которые показывает JOptionPane. В них во всех уже используются JButton. Конечно, там все без проблем кастомизируется, но эту кастомизацию нужно будет каждый раз кодировать, т.к. localization framework, построенный на наследовании, таких случаев не покрывает.
2. Заголовки JTable. Опять же можно все лопатить ручками, но геморно и ясность кода страдает.
3. Любые библиотечные компоненты, начиная с какого-нибудь календарика, не будут работать с локализацией на базе subclassing. Наверное, вы там пользуетесь библиотеками?

Ну и наконец, если вам просто написать подклассы для нескольких визуальных компонентов, то почему вместо этого не написать такое же количество подклассов для UI тех же самых компонентов?
0
3 / 3 / 7
Регистрация: 19.10.2009
Сообщений: 225
10.02.2011, 10:12  [ТС] 8
Логично
Однако и тут гемора достаточно. Как я понял, работать это будет следующим образом - компоненте в качестве текста дается определенный ключ, а ее UI по этому ключу извлекает нужный текст из базы данных и прорисовывает его. Если значения, соответствующего данному ключу, в базе нет, значит ключ и есть текст (чтобы не забивать базу данными, которые в любом используемом программой языке выглядят одинаково - например, цифры).
Однако остаются две проблемы. Первая - как после изменения языка перерисовать все видимые компонеты и вторая - скорость прорисовки. Если изменять компонеты, скорость прорисовки остается прежней, поскольку каждый компонет знает свой текст, который при изменении языка меняется всего один раз. Если же изенять UI компонет, то текст прийдется извлекать из базы данных при каждой прорисовке.
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
10.02.2011, 15:18 9
Первая проблема - перерисовать весь GUI:
Код
  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').equals(LangCo ntroller.switchCnt);
Если оба условия дают true, значение свойства 'перевод' актуально. В противном случае все свойства переприсваиваются.

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

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

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('translations');
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 19
[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(TranslatingLookAndFeel.PROP_CH ANGE_COUNT);
Long changeCountActual = TranslatingLookAndFeel.getChangeCount();
if (changeCountShouldBe == null || ! changeCountActual.equals(changeCountShouldBe)) {
underlyingUI = (LabelUI) TranslatingLookAndFeel.getUnderlyingLookAndFeel(). getDefaults().getUI(c);
}
}

private void checkLabels(JComponent c) {
String key = (String) c.getClientProperty(TranslatingLookAndFeel.PROP_LA BEL_KEY);
String labelShouldBe = (String) c.getClientProperty(TranslatingLookAndFeel.PROP_LA BEL_TEXT);
String labelActual = ((JLabel)c).getText();
Long changeCountShouldBe = (Long) c.getClientProperty(TranslatingLookAndFeel.PROP_CH ANGE_COUNT);
Long changeCountActual = TranslatingLookAndFeel.getChangeCount();
//
if (labelActual != null && !labelActual.equals('') && !(c instanceof TableCellRenderer)) {
if (! labelActual.equals(labelShouldBe)) {
key = labelActual;
labelShouldBe = TranslatingLookAndFeel.getTranslator().translate(k ey);
labelActual = labelShouldBe;
changeCountShouldBe = changeCountActual;
//
c.putClientProperty(TranslatingLookAndFeel.PROP_LA BEL_KEY, key);
c.putClientProperty(TranslatingLookAndFeel.PROP_LA BEL_TEXT, labelShouldBe);
c.putClientProperty(TranslatingLookAndFeel.PROP_CH ANGE_COUNT, changeCountShouldBe);
((JLabel)c).setText(labelActual);
} else if (! changeCountActual.
0
1 / 1 / 2
Регистрация: 07.01.2010
Сообщений: 128
15.02.2011, 08:13 20
Код
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
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
15.02.2011, 08:13
Помогаю со студенческими работами здесь

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru