File: language.pike

package info (click to toggle)
roxen 1.2beta2-3
  • links: PTS
  • area: contrib
  • in suites: slink
  • size: 16,920 kB
  • ctags: 8,589
  • sloc: ansic: 89,632; asm: 8,431; sh: 2,915; makefile: 1,784; cpp: 377
file content (390 lines) | stat: -rw-r--r-- 13,600 bytes parent folder | download | duplicates (2)
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
#include <module.h>
inherit "modules/directories/directories";

string cvs_version = "$Id: language.pike,v 1.15 1997/10/05 01:23:52 peter Exp $";
/* Is threadsafe. */

#if DEBUG_LEVEL > 20
# ifndef LANGUAGE_DEBUG
#  define LANGUAGE_DEBUG
# endif
#endif


array register_module()
{
  return ({ MODULE_DIRECTORIES | MODULE_URL | MODULE_PARSER, 
	      "Language module",
	      "Handles documents in different languages. "
	      "<p>Is also a directory module that generates no directory "
	      "listings. It must be a directory module to work, though it "
	      "could of course be fixed to make directory listings."
	      "The module works by using appropriate magic to find out what "
	      "language the user wants and then finding a file in that "
	      "language. What language a file is in is specified with an "
	      "extra extension. index.html.sv would be a file in swedish "
	      "while index.html.en would be one in english. "
	      "<p>The module also defines three new tags. "
	      "<br><b>&lt;language&gt;</b> that tells which language the "
	      "current page is in. "
	      "<br><b>&lt;available_languages&gt;</b> gives a list of other "
	      "languages the current page is in, with links to them. "
	      "<br><b>&lt;unavailable_language&gt;</b> shows the language "
	      "the user wanted, if the page was not available in that "
	      "language. "
	      "<p>All tags take the argument type={txt,img}. ",
	    ({ }), 
	    1
         });
}

void create()
{
  defvar( "default_language", "en", "Default language", TYPE_STRING,
	  "The default language for this server. Is used when trying to "
	  "decide which language to send when the user hasn't selected any. "
	  "Also the language for the files with no language-extension." );

  defvar( "languages", "en	English\nde	Deutch		en\n"
	  "sv	Svenska		en", "Languages", TYPE_TEXT_FIELD,
	  "The languages supported by this site. One language on each row. "
	  "Syntax: "
	  "<br>language-code language-name optional-next-language-codes"
	  "<br>For example:\n"
	  "<pre>sv	Svenska		en de\n"
	  "en	English		de\n"
	  "de	Deutch		en\n"
	  "</pre><p>"
	  "The next-language-code is used to determine what language should "
	  "be used in case the chosen language is unavailable. To find a "
	  "page with a suitable language the languages is tried as follows. "
	  "<ol><li>The selected language, stored as a prestate"
	  "<li>The user agent's accept-headers"
	  "<li>The selected languages next-languages-codes if any"
	  "<li>The default language"
	  "<li>If there were no selected language, the default language's "
	  "next-language-codes"
	  "<li>All languages, in the order they appear in this text-field"
	  "</ol>"
	  "<p>Empty lines, lines beginning with # or // will be ignored."
	  " Lines with errors may be ignored, or execute a HCF instruction." );

  defvar( "flag_dir", "/icons", "Flag directory", TYPE_STRING,
	  "A directory with small pictures of flags, or other symbols, "
	  "representing the various languages. Each flag should exist in the "
	  "following versions:"
	  "<dl><dt>language-code.selected.gif"
	  "<dd>Shown to indicate that the page is in that selected language, "
	  "usually by the header-module."
	  "<dt>language-code.available.gif"
	  "<dd>Shown as a link to the page in that language. Will of course "
	  "only be used if the page exists in that language."
	  "<dt>language-code.unavailable.gif"
	  "<dd>Shown to indicate that the user has selected an language that "
	  "this page hasn't been translated to."
/*	 "<dt>language-code.dir.selected.gif"
	 "<dd>Shown to indicate that the dir-entry will be shown in that "
	 "language."
	 "<dt>language-code.dir.available.gif"
	 "<dd>Shown as a link to the dir-entry translated to that language."
	 */
	  "</dl>"
	  "<p>It is of course not necessary to have all this pictures if "
	  "their use is not enabled in this module nor the header module." );

/*
  defvar( "flags_or_text", 1, "Flags in directory lists", TYPE_FLAG,
	  "If set, the directory lists will include cute flags to indicate "
	  "which language the entries exists in. Otherwise it will be shown "
	  "with not-so-cure text. " );
  defvar( "directories", 1, "Directory parsing", TYPE_FLAG, 
	  "If you set this flag to on, a directories will be "+
	  "parsed to a file-list, if no index file is present. "+
	  "If not, a 'No such file or directory' response will be generated.");
*/  
  defvar( "configp", 1, "Use config (uses prestate otherwise).",
          TYPE_FLAG,
          "If set the users chooen language will be stored using Roxens "
          "which in turn will use a Cookie stored in the browser, if "
          "possible. Unfortunatly Netscape may not reload the page when the "
          "language is changed using Cookies, which means the end-users "
          "may have to manually reload to see the page in the new language. "
          "Prestate does not have this problem, but on the other hand "
          "they will not be remembered over sessions." );

  defvar( "textonly", 0, "Text only", TYPE_FLAG,
	  "If set the tags type argument will default to txt instead of img" );
  
  defvar( "borderwidth", 0, "Border width", TYPE_INT,
	  "The width of the border around selectable flags." );

  ::create();
}

mapping parse_directory( object id )
{
  return ::parse_directory( id );
}


// language part

mapping (string:mixed) language_data = ([ ]);
array (string) language_order = ({ });
#define LANGUAGE_DATA_NAME 0
#define LANGUAGE_DATA_NEXT_LANGUAGE 1
multiset (string) language_list;
string default_language, flag_dir;
int textonly;
int borderwidth;

mixed fnord(mixed what) { return what; }


void start()
{
  string tmp;
  array (string) tmpl;

  foreach (query( "languages" ) / "\n", tmp)
    if (strlen( tmp ) > 2 && tmp[0] != '#' && tmp[0..1] != "//")
    {
      tmp = replace( tmp, "\t", " " );
      tmpl = tmp / " " - ({ "" });
      if (sizeof( tmpl ) >= 2)
      {
	language_data[ tmpl[0] ] = ({ tmpl[1], tmpl[2..] });
	language_order += ({ tmpl[0] });
      }
    }
  language_list = aggregate_multiset( @indices( language_data ) );
  foreach (indices( language_data ), tmp)
    language_data[ tmp ][ LANGUAGE_DATA_NEXT_LANGUAGE ] &= indices( language_list );
  default_language = query( "default_language" );
  textonly = query( "textonly" );
  borderwidth = query( "borderwidth" );
  
  ::start();
}

multiset (string) find_files( string url, object id )
{
  string filename, basename, extension;
  multiset (string) files = (< >);
  multiset result = (< >);
  array tmp;

  filename = reverse( (reverse( url ) / "/")[0] );
  basename = reverse( (reverse( url ) / "/")[1..] * "/" ) + "/";
  tmp = roxen->find_dir( basename, id );
  if (tmp)
    files = aggregate_multiset( @tmp );
  foreach (indices( language_list ), extension)
    if (files[ filename + "." + extension ])
      result[ extension ] = 1;
  if (files[ filename ])
    result[ "" ] = 1;
  return result;
}

mixed remap_url( object id, string url )
{
  string chosen_language, prestate_language, extension;
  string found_language;
  multiset (string) lang_tmp, found_languages, found_languages_orig;
  array (string) accept_language;

  if(id->misc->language || id->misc->in_language)
    return 0;

  id->misc->in_language=1;
  
  extension = reverse( (reverse( url ) / ".")[0] );
  if (language_list[ extension ])
  {
    string redirect_url;

    redirect_url = reverse( (reverse( url ) / ".")[1..] * "." );
    if (id->query)
      redirect_url += "?" + id->query;
    redirect_url = add_pre_state( redirect_url, (id->prestate - language_list)+
				  (< extension >) );
    redirect_url = id->conf->query( "MyWorldLocation" ) +
      redirect_url[1..];
    
    id->misc->in_language=0;
    return http_redirect( redirect_url );
  }		    
  found_languages_orig = find_files( url, id );
  found_languages = copy_value( found_languages_orig );
  if (sizeof( found_languages_orig ) == 0)
  {
    id->misc->in_language=0;
    return 0;
  }
  if (found_languages_orig[ "" ])
  {
    found_languages[ "" ] = 0;
    found_languages[ default_language ] = 1;
  }
  // The file with no language extension is supposed to be in the default
  // language

  // fill the accept_language list
  if ( accept_language = id->misc["accept-language"] )
    ;
    else
      accept_language = ({ });

#ifdef LANGUAGE_DEBUG  
  perror("Wish:%O", accept_language);
#endif
  // This looks funny, but it's nessesary to keep the order of the languages.
  accept_language = accept_language -
    ( accept_language - indices(language_list) );
#ifdef LANGUAGE_DEBUG  
  perror("Negotiated:%O\n", accept_language);
#endif

  if (query( "configp" ))
    lang_tmp = language_list & id->config;
  else
    lang_tmp = language_list & id->prestate;

#ifdef LANGUAGE_DEBUG
  if( sizeof(accept_language) )
    perror("Header-choosen language: %O\n", accept_language[0]);
#endif
  
  if (sizeof( lang_tmp ))
    chosen_language = prestate_language = indices( lang_tmp )[0];
  else if (sizeof( accept_language ))
    chosen_language = accept_language[0];
  else
    chosen_language = default_language;

#ifdef LANGUAGE_DEBUG
  perror("Presented language: %O\n", chosen_language);
#endif
  
  if (found_languages[ chosen_language ])
    found_language = chosen_language;
  else if (sizeof( accept_language & indices( found_languages ) ))
    found_language = chosen_language
      = (accept_language & indices( found_languages ))[0];
  else if (prestate_language 
	   && sizeof( fnord(language_data[ prestate_language ]
		     [ LANGUAGE_DATA_NEXT_LANGUAGE ]
		      & indices( fnord(found_languages) ) )))
    found_language
      = (language_data[ prestate_language ][ LANGUAGE_DATA_NEXT_LANGUAGE ]
	 & indices( found_languages ))[0];
  else if (found_languages[ default_language ])
    found_language = default_language;
  else if (!prestate_language 
    	   && sizeof( language_data[ default_language ]
		     [ LANGUAGE_DATA_NEXT_LANGUAGE ]
		     & indices( found_languages ) ))
    found_language
      = ((language_data[ default_language ][ LANGUAGE_DATA_NEXT_LANGUAGE ]
	 & indices( found_languages )))[0];
  else
    found_language = (language_order & indices( found_languages ))[0];

  id->misc[ "available_languages" ] = copy_value( found_languages );
  id->misc[ "available_languages" ][ found_language ] = 0;
  id->misc[ "chosen_language" ] = chosen_language;
  id->misc[ "language" ] = found_language;
  id->misc[ "flag_dir" ] = flag_dir;
  id->misc[ "language_data" ] = copy_value( language_data );
  id->misc[ "language_list" ] = copy_value( language_list );
//  id->prestate -= language_list;
//  id->prestate[ found_language ] = 1; // Is this smart?

  if (found_languages_orig[ found_language ])
    id->extra_extension += "." + found_language;
  // We don't change not_query incase it was a file without
  // extension that were found.

  id->misc->in_language=0;
  return id;
}

string tag_unavailable_language( string tag, mapping m, object id )
{
  if (!id->misc[ "chosen_language" ] || !id->misc[ "language" ]
      || !id->misc[ "language_data" ])
    return "";
  if (id->misc[ "chosen_language" ] == id->misc[ "language" ])
    return "";
  if (m[ "type" ] == "txt" || textonly && m[ "type" ] != "img")
    return id->misc[ "language_data" ][ id->misc[ "chosen_language" ] ];
  else
    return "<img src=" + query( "flag_dir" ) + id->misc[ "chosen_language" ]
            + ".unavailable.gif alt=\""
            + id->misc[ "language_data" ][ id->misc[ "chosen_language" ] ][0]
            + "\">";
}

string tag_language( string tag, mapping m, object id )
{
  if (!id->misc[ "language" ] || !id->misc[ "language_data" ]
      || !id->misc[ "language_list" ])
    return "";
  if (m[ "type" ] == "txt" || textonly && m[ "type" ] != "img")
    return id->misc[ "language_data" ][ id->misc[ "language" ] ][0];
  else
    return "<img src=" + query( "flag_dir" ) + id->misc[ "language" ]
            + ".selected.gif alt=\""
            + id->misc[ "language_data" ][ id->misc[ "language" ] ][0]
            + "\">";
}

string tag_available_languages( string tag, mapping m, object id )
{
  string result, lang;
  int c;
  array available_languages;

  if (!id->misc[ "available_languages" ] || !id->misc[ "language_data" ]
      || !id->misc[ "language_list" ])
    return "";
  result = "";
  available_languages = indices( id->misc["available_languages"] );
  for (c=0; c < sizeof( available_languages ); c++)
  {
    if (query( "configp" ))
      result += "<aconf ";
    else
      result += "<apre ";
    foreach (indices( id->misc[ "language_list" ]
		      - (< available_languages[c] >) ), lang)
      result += "-" + lang + " ";
    if (query( "configp" ))
      result += "+" + available_languages[c]
	 + (id->misc[ "index_file" ] ? " href=\"\" >" : ">");
    else
      result += available_languages[c]
	 + (id->misc[ "index_file" ] ? " href=\"\" >" : ">");
    if (m[ "type" ] == "txt" || textonly && m[ "type" ] != "img")
      result += ""+id->misc[ "language_data" ][ available_languages[c] ][0];
    else
      result += "<img src=" + query( "flag_dir" ) + available_languages[c] +
	".available.gif alt=\"" +
	id->misc[ "language_data" ][ available_languages[c] ][0] +
	"\" " + sprintf("border=%d", borderwidth) + ">";
	
    if (query( "configp" ))
      result += "</aconf>\n";
    else
      result += "</apre>\n";
    }
  return result;
}

mapping query_tag_callers()
{
  return ([ "unavailable_language" : tag_unavailable_language,
            "language" : tag_language,
            "available_languages" : tag_available_languages ]);
}