contentType и i18n сообщений Struts
От: Yarrow  
Дата: 24.12.05 10:16
Оценка:
Уважаемые, почему при явном указании кодировки JSP-страниц UTF-8 в файлай ресурсов строки на разных языках приходится писать как escape-последовательности? Почему файл ресурсов нельзя просто написать в кодировке UTF-8? Так и должно быть, или я чего-то не понимаю?

Пример:
struts-blank.war, Welcome.jsp
добавляю:
<%@ page
language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
%>

В MessageResources_ru.properties приходится писать так:
...
welcome.heading=\u0412\u044d\u043b\u043a\u0430\u043c!
...

Неудобно. Приходится файлы ресурсов на разных языках держать в двух экземплярах — в родной для языка кодировке и в ecs-последовательностях.

Если просто написать файлы ресурсов в UTF-8, на страничке сообщения будут крякозябами.

Если contentType явно вообще не указывать, а исходники лежат, например, в Windows-1251, то файл ресурсов можно написать в этой же кодировке, всё будет работать. Но не всякий браузер тогда поймёт, что страничка в Windows-1251, приходится выбирать кодировку вручную (IE понимает, а Firefox — нет).

Вобщем вопрос: есть ли возможность явно указывать кодировку странички как UTF-8 и файлы ресурсов писать в этой же кодировке? Если "да", то что для этого нужно сделать?
Re: contentType и i18n сообщений Struts
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 24.12.05 10:39
Оценка: +1
Yarrow,

Y>В MessageResources_ru.properties приходится писать так:

Y>...
Y>welcome.heading=\u0412\u044d\u043b\u043a\u0430\u043c!
Y>...

Ручками? Это ужасно Советую попробовать хотя бы так:
native2ascii -encoding utf-8 c:\src.txt c:\dst.txt

Можно антом — вообще прелесть.

Y>Неудобно. Приходится файлы ресурсов на разных языках держать в двух экземплярах — в родной для языка кодировке и в ecs-последовательностях.


Зато переносимо. И никаких проблем с символами завершения строк.

Y>Если просто написать файлы ресурсов в UTF-8, на страничке сообщения будут крякозябами.


Грабли в Properties#load(InputStream). Энтот класс ожидает входной файл в ISO 8859-1, поскольку невозможно (негде) указать кодировку входного файла пропертей.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[2]: contentType и i18n сообщений Struts
От: Yarrow  
Дата: 24.12.05 11:18
Оценка:
LCR>Ручками? Это ужасно
Согласен, использую native2ascii . Про ant не знал, не пользуюсь. А в чём прелесть? При сборке сам перекодирует properties в esc?

Но, вернёмся к барашкам:
LCR>Грабли в Properties#load(InputStream). Энтот класс ожидает входной файл в ISO 8859-1, поскольку невозможно (негде) указать кодировку входного файла пропертей.

Вынужден повториться:
Если contentType явно вообще не указывать, а исходники лежат, например, в Windows-1251, то файл ресурсов можно написать в этой же кодировке, всё будет работать.

Очевидно, что struts умеет восстанавливать кодировку properties-файла (пишешь файл в Windows-1251, страничка правильно отображается в Windows-1251, не смотря на особенность Properties.load()). Но если явно указать contentType на страничке, то уже не работает, кодировка восстанавливается не правильно.

Вопросы те же: можно ли явно указать кодировку UTF-8 в contentType и держать файлы ресурсов в UTF-8? Если можно, то что для этого нужно сделать?
Re[3]: contentType и i18n сообщений Struts
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 24.12.05 12:52
Оценка:
Yarrow,

LCR>>Ручками? Это ужасно

Y>Согласен, использую native2ascii . Про ant не знал, не пользуюсь. А в чём прелесть? При сборке сам перекодирует properties в esc?
Да просто есть соответствующий таск.

Y>Вынужден повториться:

Y>Если contentType явно вообще не указывать, а исходники лежат, например, в Windows-1251, то файл ресурсов можно написать в этой же кодировке, всё будет работать.

Будет работать на вашей машине. На машине с другой локалью —

Y>Очевидно, что struts умеет восстанавливать кодировку properties-файла (пишешь файл в Windows-1251, страничка правильно отображается в Windows-1251, не смотря на особенность Properties.load()). Но если явно указать contentType на страничке, то уже не работает, кодировка восстанавливается не правильно.


Это не struts умеет восстанавливать кодировку в properties файле, а поток байтов просасывается через struts в неизменном виде. То есть из properties загружается во внутренний объект Properties, а потом через библиотеку тэгов выводится в HTML (<html:text ... >). А там уже работает автораспознавание кодировки в браузере.

Y>Вопросы те же: можно ли явно указать кодировку UTF-8 в contentType и держать файлы ресурсов в UTF-8? Если можно, то что для этого нужно сделать?


есть маленькая идейка...
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[4]: contentType и i18n сообщений Struts
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 26.12.05 08:58
Оценка:
Lazy Cjow Rhrr,

Y>>Вопросы те же: можно ли явно указать кодировку UTF-8 в contentType и держать файлы ресурсов в UTF-8? Если можно, то что для этого нужно сделать?


LCR>есть маленькая идейка...


К сожалению, индейка не прошла Хотел попробовать фильтр прикрутить.

К слову сказать, мне тоже захотелось знать железный способ хранения ресурсов в UTF-8 (железный, то есть не зависящий от локалей и осей).
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[5]: contentType и i18n сообщений Struts
От: Lucker Беларусь http://lucker.intervelopers.com/
Дата: 26.12.05 09:12
Оценка: +1
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR>К слову сказать, мне тоже захотелось знать железный способ хранения ресурсов в UTF-8 (железный, то есть не зависящий от локалей и осей).


xml
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Re[5]: contentType и i18n сообщений Struts
От: StanislavK Великобритания  
Дата: 26.12.05 09:31
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR>Lazy Cjow Rhrr,


Y>>>Вопросы те же: можно ли явно указать кодировку UTF-8 в contentType и держать файлы ресурсов в UTF-8? Если можно, то что для этого нужно сделать?

LCR>>есть маленькая идейка...
LCR>К сожалению, индейка не прошла Хотел попробовать фильтр прикрутить.
LCR>К слову сказать, мне тоже захотелось знать железный способ хранения ресурсов в UTF-8 (железный, то есть не зависящий от локалей и осей).

XML или java файл.
Re[6]: contentType и i18n сообщений Struts
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 26.12.05 10:53
Оценка:
Здравствуйте, Lucker, Вы писали:

L>Здравствуйте, Lazy Cjow Rhrr, Вы писали:


LCR>>К слову сказать, мне тоже захотелось знать железный способ хранения ресурсов в UTF-8 (железный, то есть не зависящий от локалей и осей).


L>xml


Struts и интернацонализация будут работать с пропертями в xml формате?
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[6]: contentType и i18n сообщений Struts
От: Yarrow  
Дата: 27.12.05 18:28
Оценка:
SK>XML или java файл.
А можно подробнее? Struts сам сможет считывать ресурсы из XML?? И что вы имеете ввиду, говоря о java-файле?
Re[7]: contentType и i18n сообщений Struts
От: StanislavK Великобритания  
Дата: 28.12.05 11:52
Оценка:
Здравствуйте, Yarrow, Вы писали:

SK>>XML или java файл.

Y>А можно подробнее? Struts сам сможет считывать ресурсы из XML?? И что вы имеете ввиду, говоря о java-файле?

С XML, мне кажется, что я погорячился, но все с умным видом промолчали
А с java файлами все просто — пишешь класс, наследуешь от ListResourceBundle, дальше по доке. В файл пишелся все в utf8 и компилятору указывается, что исходники в utf8 и все.
Re: contentType и i18n сообщений Struts
От: Аноним  
Дата: 09.01.06 12:14
Оценка:
Здравствуйте, Yarrow, Вы писали:

Y>Уважаемые, почему при явном указании кодировки JSP-страниц UTF-8 в файлай ресурсов строки на разных языках приходится писать как escape-последовательности? Почему файл ресурсов нельзя просто написать в кодировке UTF-8? Так и должно быть, или я чего-то не понимаю?

.......

Вариант настроиться на базу и хранить в ней ресурсы, т.е.

<message-resources factory="com.ipte.shop.msgresources.DBMessageResourceFactory" parameter="MessageResources" />
--------------------------------------------------------------------------------------
package com.ipte.shop.msgresources;

import org.apache.struts.util.MessageResourcesFactory;
import org.apache.struts.util.MessageResources;

public final class DBMessageResourceFactory extends MessageResourcesFactory {
public MessageResources createResources(String config) {
return new DBMessageResources(this, config, this.returnNull);
}
}

--------------------------------------------------------------------------------------
package com.ipte.shop.msgresources;

import org.apache.struts.util.MessageResources;
import org.apache.struts.util.MessageResourcesFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Locale;
import java.util.WeakHashMap;
import java.util.HashMap;
import java.sql.*;

import com.ipte.shop.Constants;
import com.ipte.shop.controllers.AbstractController;
import org.hibernate.Session;

public final class DBMessageResources extends MessageResources {
private static final Log LOG = LogFactory.getLog(DBMessageResources.class);

private HashMap<Locale,WeakHashMap<String,String>> locales = new HashMap<Locale, WeakHashMap<String,String>>(3);

public DBMessageResources(MessageResourcesFactory messageResourcesFactory, String string) {
super(messageResourcesFactory, string);
}

public DBMessageResources(MessageResourcesFactory messageResourcesFactory, String string, boolean b) {
super(messageResourcesFactory, string, b);
}

public String getMessage(Locale locale, String key) {
WeakHashMap<String,String> resourcesMap = locales.get(locale);
if(resourcesMap == null) {
resourcesMap = new WeakHashMap<String, String>(500);
locales.put(locale, resourcesMap);
}

String resource = resourcesMap.get(key);

if(resource == null) {
try {
resource = loadResource(locale, key);
resourcesMap.put(key, resource);
} catch (ResourceNotFoundException e) {
resource = e.getMessage();
}
}

return resource;
}

private String loadResource(Locale locale, String key) throws ResourceNotFoundException{
String resourceValue = (returnNull) ? null : "";

String language = locale.getLanguage();
String defaultLanguage = Constants.DEFAULT_LOCALE.getLanguage();
LOG.warn("Loading resource: " + language + " : " + defaultLanguage + " : " + key);
Session session = null;
Connection connection;
PreparedStatement pst = null;
ResultSet rs = null;
ResourceNotFoundException exc = null;
try {
session = AbstractController.openSession();
connection = session.connection();
pst = connection.
prepareStatement("SELECT LOCALE, RESOURCE_VALUE FROM SYS_MESSAGE_RESOURCES WHERE RESOURCE_KEY=? AND LOCALE IN (?, ?)");

pst.setString(1, key);
pst.setString(2, language);
pst.setString(3, defaultLanguage);

rs = pst.executeQuery();

if(rs.next()) {
String lang = rs.getString("LOCALE");
resourceValue = rs.getString("RESOURCE_VALUE");
if(lang.equals(defaultLanguage) && rs.next()) {
resourceValue = rs.getString("RESOURCE_VALUE");
}
} else {
exc = new ResourceNotFoundException("Resource with locale = [" + language + "] key = [" + key + "] not found");
}
} catch (Exception e) {
LOG.error("Error loading resource.", e);
} finally {
try {
if(rs!=null) rs.close();
if(pst!=null) pst.close();
if(session!=null)session.close();
} catch (Exception e) {
LOG.error("Session closing error.", e);
}
}
if(exc!=null) throw exc;
return resourceValue;
}
}
============================================================================================
package com.ipte.shop.msgresources;

public final class ResourceNotFoundException extends Exception {
public ResourceNotFoundException(String message) {
super(message);
}
}
Re[5]: contentType и i18n сообщений Struts
От: MaximWirt Россия http://itodo.mawisoft.com
Дата: 09.01.06 20:28
Оценка:
LCR>К слову сказать, мне тоже захотелось знать железный способ хранения ресурсов в UTF-8 (железный, то есть не зависящий от локалей и осей)

в ant можно указать кодировку

<native2ascii encoding="UTF-8" src="src"
dest="classes"
includes="**/res/Resources*.properties"/>

все новые IDE позволяют настроить, чтобы properties файлы сохранялись на диске в UTF-8, в частности в IDEA и Eclipse это делается.
Re[6]: contentType и i18n сообщений Struts
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 10.01.06 08:03
Оценка:
MaximWirt,

LCR>>К слову сказать, мне тоже захотелось знать железный способ хранения ресурсов в UTF-8 (железный, то есть не зависящий от локалей и осей)


MW>в ant можно указать кодировку


MW><native2ascii encoding="UTF-8" src="src"

MW> dest="classes"
MW> includes="**/res/Resources*.properties"/>

Человеку (да и мне уже тоже ) интересно, можно ли ресурсы хранить в UTF-8, и подсунуть их Struts? Без native2ascii. То, что хранить xml в UTF-8 можно, это и барану ясно.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[7]: contentType и i18n сообщений Struts
От: Lucker Беларусь http://lucker.intervelopers.com/
Дата: 10.01.06 10:00
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR>MaximWirt,


LCR>Человеку (да и мне уже тоже ) интересно, можно ли ресурсы хранить в UTF-8, и подсунуть их Struts? Без native2ascii. То, что хранить xml в UTF-8 можно, это и барану ясно.

полагаю в том виде в котором поставляется стратс сейчас — нет. Однако фича была запланирована аж в 2000 году. Но видно никому так и не понадобилась, или вылилась в нечто более глобальное, пока еще к стратсу не прикрученное. Если поискать в гугле — то можно найти ссылки на некоторые реализации XMLMessageResources, но меня они ни к чему конкретномуне привели. Так что надо или рыть глубже или писать самому.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Re[2]: contentType и i18n сообщений Struts Windows-1251
От: bedward70 Россия http://www.bedward70.narod.ru/
Дата: 30.01.06 08:05
Оценка: 5 (1)
Здравствуйте, Аноним, Вы писали:

Боагодарю, ваша идея была отправной точкой.
Хотелось бы поделиться своим опытом для чтения файла по умолчанию из "Windows-1251":
(Лазил по первоисточникам. Что касается Copyright: привожу код не пренадлежащим
мне код не для корысти, а только для демонстрации )

1. В struts-config.xml в секции message-resources прописал в
factory свой класс по загрузке файла установок

<message-resources factory="ru.narod.bedward70.bevdata.struts.MessageResourceFactoryWin"
parameter="ApplicationResources" />

Обратите внимание на параметр имени файла "ApplicationResources", у меня их два :
ApplicationResources.properties — для универсальный
ApplicationResources_ru.properties — для RU-локали, здесь у меня сохранен русский текст в кодировке "Windows-1251".

2. Создал свой factory-класс для определения класса обработки файла установок

package ru.narod.bedward70.bevdata.struts;

import org.apache.struts.util.MessageResourcesFactory;
import org.apache.struts.util.MessageResources;

public class MessageResourceFactoryWin
    extends MessageResourcesFactory {
    public MessageResources createResources(String config) {
    return new MessageResourcesWin(this, config, this.returnNull);
    }
}


3. Создал свой класс загрузки от PropertyMessageResources, переопределяю конструкторы и метод loadLocale, заменяю только
Properties props = new Properties(); на PropertiesCharset props = new PropertiesCharset();

props.load(is); на props.load(is,"Windows-1251");

Вот полный текст:

package ru.narod.bedward70.bevdata.struts;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.util.MessageResourcesFactory;
import org.apache.struts.util.PropertyMessageResources;

public class MessageResourcesWin extends PropertyMessageResources {
    private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(MessageResourcesWin.class);

    public MessageResourcesWin(MessageResourcesFactory messageResourceFactory, String config, boolean returnNull) {
        super(messageResourceFactory,config,returnNull);
    }
    public MessageResourcesWin(MessageResourcesFactory messageResourceFactory, String config) {
        super(messageResourceFactory,config);
    }

    protected synchronized void loadLocale(String localeKey)
    {
        if(log.isTraceEnabled())
            log.trace("loadLocale(" + localeKey + ")");
        if(locales.get(localeKey) != null)
            return;
        locales.put(localeKey, localeKey);
        String name = config.replace('.', '/');
        if(localeKey.length() > 0)
            name = name + "_" + localeKey;
        name = name + ".properties";
        InputStream is = null;
        PropertiesCharset props = new PropertiesCharset();
        if(log.isTraceEnabled())
            log.trace("  Loading resource '" + name + "'");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if(classLoader == null)
            classLoader = getClass().getClassLoader();
        is = classLoader.getResourceAsStream(name);
        if(is != null)
            try
            {
                props.load(is,"Windows-1251");
            }
            catch(IOException e)
            {
                log.error("loadLocale()", e);
            }
            finally
            {
                try
                {
                    is.close();
                }
                catch(IOException e)
                {
                    log.error("loadLocale()", e);
                }
            }
        if(log.isTraceEnabled())
            log.trace("  Loading resource completed");
        if(props.size() < 1)
            return;
        synchronized(messages)
        {
            String key;
            for(Iterator names = props.keySet().iterator(); names.hasNext(); messages.put(messageKey(localeKey, key), props.getProperty(key)))
            {
                key = (String)names.next();
                if(log.isTraceEnabled())
                    log.trace("  Saving message key '" + messageKey(localeKey, key));
            }
        }
    }
}


4. Учу читать Prorepties в другой кодировке (делаю класс от Prorepties)
Делаю копию метода load, но с параметром кодировки, и переопределяю первую строку
BufferedReader in = new BufferedReader(new InputStreamReader(inStream, "8859_1"));
на
BufferedReader in = new BufferedReader(new InputStreamReader(inStream, charsetName));
Но в ошибки вылезли закрытые переменные и методы базового класса — пришлось из переопределить
Вот полный текст:

package ru.narod.bedward70.bevdata.struts;

import java.util.Properties;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;

public class PropertiesCharset  extends Properties {
    private static final String whiteSpaceChars = " \t\r\n\f";
    private static final String keyValueSeparators = "=: \t\r\n\f";
    private static final String strictKeyValueSeparators = "=:";



    private boolean continueLine(String line) {
        int slashCount = 0;
        int index = line.length() - 1;
        while ((index >= 0) && (line.charAt(index--) == '\\'))
            slashCount++;
        return (slashCount % 2 == 1);
    }


    public synchronized void load(InputStream inStream, String charsetName) throws IOException {

   BufferedReader in = new BufferedReader(new InputStreamReader(inStream, charsetName));

    while (true) {
        // Get next line
        String line = in.readLine();
        if (line == null)
            return;

        if (line.length() > 0) {

            // Find start of key
            int len = line.length();
            int keyStart;
            for (keyStart=0; keyStart<len; keyStart++)
                if (whiteSpaceChars.indexOf(line.charAt(keyStart)) == -1)
                    break;

            // Blank lines are ignored
            if (keyStart == len)
                continue;

            // Continue lines that end in slashes if they are not comments
            char firstChar = line.charAt(keyStart);
            if ((firstChar != '#') && (firstChar != '!')) {
                while (continueLine(line)) {
                    String nextLine = in.readLine();
                    if (nextLine == null)
                        nextLine = "";
                    String loppedLine = line.substring(0, len-1);
                    // Advance beyond whitespace on new line
                    int startIndex;
                    for (startIndex=0; startIndex<nextLine.length(); startIndex++)
                        if (whiteSpaceChars.indexOf(nextLine.charAt(startIndex)) == -1)
                            break;
                    nextLine = nextLine.substring(startIndex,nextLine.length());
                    line = new String(loppedLine+nextLine);
                    len = line.length();
                }

                // Find separation between key and value
                int separatorIndex;
                for (separatorIndex=keyStart; separatorIndex<len; separatorIndex++) {
                    char currentChar = line.charAt(separatorIndex);
                    if (currentChar == '\\')
                        separatorIndex++;
                    else if (keyValueSeparators.indexOf(currentChar) != -1)
                        break;
                }

                // Skip over whitespace after key if any
                int valueIndex;
                for (valueIndex=separatorIndex; valueIndex<len; valueIndex++)
                    if (whiteSpaceChars.indexOf(line.charAt(valueIndex)) == -1)
                        break;

                // Skip over one non whitespace key value separators if any
                if (valueIndex < len)
                    if (strictKeyValueSeparators.indexOf(line.charAt(valueIndex)) != -1)
                        valueIndex++;

                // Skip over white space after other separators if any
                while (valueIndex < len) {
                    if (whiteSpaceChars.indexOf(line.charAt(valueIndex)) == -1)
                        break;
                    valueIndex++;
                }
                String key = line.substring(keyStart, separatorIndex);
                String value = (separatorIndex < len) ? line.substring(valueIndex, len) : "";

                // Convert then store key and value
                key = loadConvert(key);
                value = loadConvert(value);
                put(key, value);
            }
        }
    }
    }

    private String loadConvert(String theString) {
        char aChar;
        int len = theString.length();
        StringBuffer outBuffer = new StringBuffer(len);

        for (int x=0; x<len; ) {
            aChar = theString.charAt(x++);
            if (aChar == '\\') {
                aChar = theString.charAt(x++);
                if (aChar == 'u') {
                    // Read the xxxx
                    int value=0;
            for (int i=0; i<4; i++) {
                aChar = theString.charAt(x++);
                switch (aChar) {
                  case '0': case '1': case '2': case '3': case '4':
                  case '5': case '6': case '7': case '8': case '9':
                     value = (value << 4) + aChar - '0';
                 break;
              case 'a': case 'b': case 'c':
                          case 'd': case 'e': case 'f':
                 value = (value << 4) + 10 + aChar - 'a';
                 break;
              case 'A': case 'B': case 'C':
                          case 'D': case 'E': case 'F':
                 value = (value << 4) + 10 + aChar - 'A';
                 break;
              default:
                              throw new IllegalArgumentException(
                                           "Malformed \\uxxxx encoding.");
                        }
                    }
                    outBuffer.append((char)value);
                } else {
                    if (aChar == 't') aChar = '\t';
                    else if (aChar == 'r') aChar = '\r';
                    else if (aChar == 'n') aChar = '\n';
                    else if (aChar == 'f') aChar = '\f';
                    outBuffer.append(aChar);
                }
            } else
                outBuffer.append(aChar);
        }
        return outBuffer.toString();
    }

}


Ну как бы и все , надеюсь мой опыт поможет вам.
С уважением, Эдвард
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.