OSDN Git Service

マルチプロジェクト型にレポジトリを変更するために移動した
[toppersasp4lpc/asp.git] / asp / cfg / toppers / gettext.cpp
1 /*
2  *  TOPPERS Software
3  *      Toyohashi Open Platform for Embedded Real-Time Systems
4  *
5  *  Copyright (C) 2007-2008 by TAKAGI Nobuhisa
6  * 
7  *  上記著作権者は,以下の(1)〜(4)の条件を満たす場合に限り,本ソフトウェ
8  *  ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
9  *  変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
10  *  (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
11  *      権表示,この利用条件および下記の無保証規定が,そのままの形でソー
12  *      スコード中に含まれていること.
13  *  (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
14  *      用できる形で再配布する場合には,再配布に伴うドキュメント(利用
15  *      者マニュアルなど)に,上記の著作権表示,この利用条件および下記
16  *      の無保証規定を掲載すること.
17  *  (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
18  *      用できない形で再配布する場合には,次のいずれかの条件を満たすこ
19  *      と.
20  *    (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
21  *        作権表示,この利用条件および下記の無保証規定を掲載すること.
22  *    (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
23  *        報告すること.
24  *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
25  *      害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
26  *      また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
27  *      由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを
28  *      免責すること.
29  * 
30  *  本ソフトウェアは,無保証で提供されているものである.上記著作権者お
31  *  よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
32  *  に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
33  *  アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
34  *  の責任を負わない.
35  * 
36  */
37 #include <cstdlib>
38 #include <stdlib.h>     // Cygwin対策
39 #include <cstring>
40 #include <clocale>
41 #include <string>
42 #include <map>
43 #include <fstream>
44 #include <stdexcept>
45 #include "toppers/gettext.hpp"
46 #include "toppers/cpp.hpp"
47 #include "toppers/global.hpp"
48 #include <boost/scoped_array.hpp>
49 #include <boost/any.hpp>
50 #include <boost/filesystem/path.hpp>
51 #include <boost/filesystem/operations.hpp>
52
53 namespace toppers
54 {
55   namespace
56   {
57
58     std::map< std::string, std::string > msgcat;
59
60     void register_msgcat( std::string const& msgid, std::string const& msgstr )
61     {
62       std::string::size_type size = msgstr.size();
63       boost::scoped_array< wchar_t > wbuf( new wchar_t[ size + 1 ] );
64       boost::scoped_array< char > buf( new char[ size + 1 ] );
65       wchar_t* wcs = wbuf.get();
66       wchar_t wc = 0;
67       for ( std::string::const_iterator iter( msgstr.begin() ), last( msgstr.end() ); iter != last; ++iter )
68       {
69         int c = static_cast< unsigned char >( *iter );
70         if ( ( ( c & 0xc0 ) == 0xc0 ) || ( c < 0x80 ) ) // 先行バイト
71         {
72           if ( wc != 0 )
73           {
74             *wcs++ = wc;
75             wc = 0;
76           }
77           if ( ( c & 0x80 ) == 0 )
78           {
79             wc = static_cast< wchar_t >( c );
80           }
81           else if ( ( c & 0xe0 ) == 0xc0 )
82           {
83             wc = static_cast< wchar_t >( c & 0x1f );
84           }
85           else if ( ( c & 0xf0 ) == 0xe0 )
86           {
87             wc = static_cast< wchar_t >( c & 0xf );
88           }
89           else
90           {
91             // サロゲートは未対応(ここで対応したとしても、文字コード変換時にしくじる可能性大)
92           }
93         }
94         else    // 後続バイト
95         {
96           wc = static_cast< wchar_t >( ( wc << 6 ) | ( c & 0x3f ) );
97         }
98       }
99       if ( wc != 0 )
100       {
101         *wcs++ = wc;
102       }
103       *wcs = L'\0';
104
105       // ↓ この間は決して例外が発生しない
106       char const* locale = std::setlocale( LC_CTYPE, "" );
107       /* std:: */wcstombs( buf.get(), wbuf.get(), size + 1 );     // Unicode から環境依存の文字コードへ変換
108       std::setlocale( LC_CTYPE, locale );
109       // ↑ この間は決して例外が発生しない
110
111       msgcat[ msgid ] = std::string( buf.get() );
112     }
113
114     bool msgcat_loaded = false;
115
116   }
117
118   /*!
119    *  \brief      メッセージカタログのロード
120    *  \param[in]  dir     *.po ファイルが存在するディレクトリ
121    *  \retval     true    成功
122    *  \retval     false   失敗
123    *
124    *  実装を簡便化するため、.poファイルの記述方法には以下の制約がある。
125    *  - msgid, msgstr は必ず行の先頭に記述する。
126    *  - msgid, msgstr の直後には、必ず空白類文字一文字とし、その直後に文字列を記述する。
127    *  - 文字列のみを記述する行は必ず " で始める。
128    *  - .poファイルはの文字コードは必ず UTF-8N とする。
129    */
130   bool load_msgcat( std::string const& dir )
131   {
132     namespace fs = boost::filesystem;
133     char const* env = std::getenv( "TOPPERS_CFG_LANG" );
134 //      env = "ja";
135     if ( env == 0 )
136     {
137       return false;
138     }
139     std::string lang( env );
140
141     fs::path cfg_dir( dir, fs::native );
142     fs::path po_file( cfg_dir/fs::path( lang + ".po", fs::native ) );
143     std::ifstream ifs( po_file.native_file_string().c_str() );
144     std::string msgid;
145     std::string msgstr;
146
147     while ( ifs )
148     {
149       std::string str;
150       std::getline( ifs, str );
151
152       // 改行文字の違いを吸収
153       std::string::size_type pos = str.find_last_not_of( " \t\r\n" ); // ついでに行末の空白類も除去
154       if ( pos != std::string::npos && pos < str.size() - 1 )
155       {
156         char c = str[pos];
157         str.erase( pos + 1, std::string::npos );
158       }
159
160       if ( str.empty() || str[ 0 ] == '#' || str == "" )
161       {
162         ;   // 空行またはコメント行
163       }
164       else
165       {
166         try
167         {
168           if ( std::strncmp( str.c_str(), "msgid", sizeof( "msgid" )-1 ) == 0 )
169           {
170             str.erase( 0, sizeof( "msgid" )-1+1 );
171             msgid = expand_quote( str );           
172           }
173           else if ( std::strncmp( str.c_str(), "msgstr", sizeof( "msgstr" )-1 ) == 0 )
174           {
175             str.erase( 0, sizeof( "msgstr" )-1+1 );
176             msgstr = expand_quote( str );
177           }
178           else
179           {
180             msgstr += expand_quote( str );
181           }
182           if ( !msgid.empty() && !msgstr.empty() )  // 直前の msgid / msgstr を登録
183           {
184             register_msgcat( msgid, msgstr );
185             msgid.clear();
186             msgstr.clear();
187           }
188         }
189         catch ( std::invalid_argument& )
190         {
191           return false;
192         }
193       }
194     }
195     msgcat_loaded = true;
196     return true;
197   }
198
199   /*!
200    *  \brief      メッセージの翻訳
201    *  \param[in]  message   メッセージID
202    *  \return     翻訳後のメッセージ
203    */
204   std::string const& gettext( std::string const& message )
205   {
206     static bool f = load_msgcat( get_global< std::string >( "cfg-directory" ) );
207     if ( !msgcat_loaded )
208     {
209       return message;
210     }
211
212     std::map< std::string, std::string >::const_iterator iter( msgcat.find( message ) ), last( msgcat.end() );
213     if ( iter != last )
214     {
215       std::string const& result( iter->second );
216       return result;
217     }
218     return message;
219   }
220
221 }