locale.module 12.1 KB
Newer Older
Dries's avatar
   
Dries committed
1
<?php
2
// $Id$
Dries's avatar
   
Dries committed
3

Dries's avatar
   
Dries committed
4
function locale_help() {
Dries's avatar
   
Dries committed
5
 ?>
Dries's avatar
   
Dries committed
6
  <p>Normally programs are written and documented in English, and use English to interact with users.  This is true for a great deal of websites.  However, most people are less comfortable with English than with their own native language, and would prefer to use their mother tongue as much as possible.  Many people love see their website showing a lot less of English, and far more of their own language.</p>
Dries's avatar
   
Dries committed
7
  <p>Therefore Drupal provides a framework to setup a multi-lingual website, or to overwrite the default texts in English.  We explored the various alternatives to support internationalization and decided to design the framework in such a way that the impact of internationalization on drupal's sources is minimized, modular and that it doesn't require a HTML or PHP wizard to maintain translations.  Maintaining translations had to be simple so it became as easy as filling out forms on the administration page.</p>
Dries's avatar
   
Dries committed
8

Dries's avatar
   
Dries committed
9
  <h3>How to translate texts</h3>
Dries's avatar
   
Dries committed
10

Dries's avatar
   
Dries committed
11
12
13
14
15
16
17
  <p>The actual translation starts at the "overview" of the locale page of the administration pages.  To allow a user to maintain the translations, he obviously needs access to the locale module.  See the account documentation for more information on roles and permissions.</p>
  <p>At the locale page, users with the proper access rights will see the various texts that need translation on the left column of the table.</p>
  <p>Below the text you can see an URI where this text shows up one your site.  Changes are most of these texts will be used and displayed on more than one page, though only one example URI is presented.</p>
  <p>The second column displays the supported languages as defined in the configuration file.  See below for more information on how to support new languages.  If the symbol for a language is seen like <strike>this</strike>, it means that this entry still needs to be translated into that language.  If not, it has been translated already.</p>
  <p>To add or change a translation click the "edit locale" link in the third column, the "operations" column.  You'll be presented the original text and fields for translation in the supported languages.  Enter the translations and confirm by clicking the "Save translations" button.  The translations need not be accurate; they are for your site so you can choose what to show to your users.</p>
  <p>To delete a translation, click the "delete locale" link at the overview page and the translation will be immediately deleted without confirmation.  Deleting translations is convenient for removing texts that belonged to an obsolete module.</p>
  <p>In some texts special strings such as "%a" and "%b" show up.  Those get replaced by some string at run-time when Drupal dynamically generate pages.  You can find out which string this is by looking at the page where the text appears.  This is where the above mentioned URI can come in handy.</p>
Dries's avatar
   
Dries committed
18

Dries's avatar
   
Dries committed
19
  <h3>How to add new languages</h3>
Dries's avatar
   
Dries committed
20

Dries's avatar
   
Dries committed
21
22
  <p>Adding a new language requires you to edit your configuration file and to edit your SQL database.  Assuming you want to support Dutch (ISO 639 code: "nl") and French (ISO 639 code: "fr"), you add the following line to your configuration file's <code>$languages</code>-variable:</p>
  <pre>
Dries's avatar
   
Dries committed
23
    $languages = array("nl" => "Dutch / Nederlands", "fr" => "French / Francais");
Dries's avatar
   
Dries committed
24
25
26
  </pre>
  <p>Note that the default language must come first and that if you want to overwrite the default text you can add an entry for English (ISO 639 code: "en"):</p>
  <pre>
Dries's avatar
   
Dries committed
27
    $languages = array("en" => "English", "nl" => "Dutch / Nederlands", "fr" => "French / Francais");
Dries's avatar
   
Dries committed
28
29
30
  </pre>
  <p>After having edited your configuration file, make sure your SQL table "locales" has the required database fields setup to host your new translations.  You can add the required rows to your "locales" table from the MySQL prompt:</p>
  <pre>
Dries's avatar
   
Dries committed
31
32
33
    mysql> ALTER TABLE locales ADD en TEXT DEFAULT '' NOT NULL;
    mysql> ALTER TABLE locales ADD nl TEXT DEFAULT '' NOT NULL;
    mysql> ALTER TABLE locales ADD fr TEXT DEFAULT '' NOT NULL;
Dries's avatar
   
Dries committed
34
  </pre>
Dries's avatar
   
Dries committed
35
 <?php
Dries's avatar
   
Dries committed
36
37
}

38
function locale_system($field){
Kjartan's avatar
Kjartan committed
39
  $system["description"] = t("Enables the translation of the user interface to languages other than English.");
40
41
42
  return $system[$field];
}

Dries's avatar
   
Dries committed
43
function locale_perm() {
Dries's avatar
   
Dries committed
44
45
46
47
  return array("administer locales");
}

function locale_link($type) {
Dries's avatar
   
Dries committed
48
49
  global $languages;

Dries's avatar
   
Dries committed
50
  if ($type == "admin" && user_access("administer locales")) {
Dries's avatar
   
Dries committed
51
52
53
54
55
56
57
    $help["general"] = "To be written: description of locale module.  Anyone?";

    menu("admin/locale", "localization", NULL, $help["general"], 5);
    menu("admin/locale/search", "search string", "locale_admin", NULL, 8);
    menu("admin/locale/help", "help", "locale_help", NULL, 9);
    menu("admin/locale/edit", "edit string", "locale_admin", NULL, 0, 1); // hidden menu
    menu("admin/locale/delete", "delete string", "locale_admin", NULL, 0, 1); // hidden menu
Dries's avatar
   
Dries committed
58

Dries's avatar
   
Dries committed
59
    foreach ($languages as $key => $value) {
Dries's avatar
   
Dries committed
60
61
62
      menu("admin/locale/$key", "$value", NULL, $help["general"]);
      menu("admin/locale/$key/translated", "translated strings", "locale_admin");
      menu("admin/locale/$key/untranslated", "untranslated strings", "locale_admin");
Dries's avatar
   
Dries committed
63
64
    }
  }
Dries's avatar
   
Dries committed
65
66
}

Dries's avatar
   
Dries committed
67
function locale_delete($lid) {
Dries's avatar
Dries committed
68
  db_query("DELETE FROM locales WHERE lid = '%d'", $lid);
Dries's avatar
   
Dries committed
69
  locale_refresh_cache();
Dries's avatar
   
Dries committed
70
71

  return t("deleted string");
Dries's avatar
   
Dries committed
72
73
}

Dries's avatar
   
Dries committed
74
75
function locale_save($lid) {
  global $edit;
Dries's avatar
   
Dries committed
76
  foreach ($edit as $key=>$value) {
Dries's avatar
Dries committed
77
    db_query("UPDATE locales SET $key = '%s' WHERE lid = '%d'", $value, $lid);
Dries's avatar
   
Dries committed
78
79
  }
  locale_refresh_cache();
Dries's avatar
   
Dries committed
80
81
  // delete form data so it will remember where it came from
  $edit = '';
Dries's avatar
   
Dries committed
82
83

  return t("saved string");
Dries's avatar
   
Dries committed
84
85
86
87
88
89
}

function locale_refresh_cache() {
  global $languages;

  foreach (array_keys($languages) as $locale) {
Dries's avatar
   
Dries committed
90
    $result = db_query("SELECT string, %s FROM locales", $locale);
Dries's avatar
   
Dries committed
91
92
93
94
    while ($data = db_fetch_object($result)) {
      $t[$data->string] = $data->$locale;
    }
    cache_set("locale:$locale", serialize($t));
Dries's avatar
   
Dries committed
95
96
97
  }
}

Dries's avatar
   
Dries committed
98
function locale_edit($lid) {
Dries's avatar
   
Dries committed
99
  global $languages;
Dries's avatar
   
Dries committed
100

Dries's avatar
   
Dries committed
101
  $result = db_query("SELECT * FROM locales WHERE lid = '$lid'");
Dries's avatar
   
Dries committed
102
  if ($translation = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
103
    $form .= form_item(t("Original text"), "<pre>". wordwrap($translation->string) ."</pre>");
Dries's avatar
   
Dries committed
104
    foreach ($languages as $code=>$language) $form .= (strlen($translation->string) > 30) ? form_textarea($language, $code, $translation->$code, 50, 10) : form_textfield($language, $code, $translation->$code, 50, 128);
Dries's avatar
   
Dries committed
105
    $form .= form_submit(t("Save translations"));
Dries's avatar
   
Dries committed
106

Dries's avatar
   
Dries committed
107
    return form($form);
Dries's avatar
   
Dries committed
108
109
110
111
  }
}

function locale_languages($translation) {
Dries's avatar
   
Dries committed
112
113
  global $languages;

Dries's avatar
   
Dries committed
114
115
  foreach ($languages as $key => $value) {
    $output .= ($translation->$key) ? "<a href=\"#\" title=\"". $translation->$key ."\">$key</a> " : "<strike>$key</strike> ";
Dries's avatar
   
Dries committed
116
117
118
119
120
  }

  return $output;
}

Kjartan's avatar
Kjartan committed
121
function locale_seek() {
Dries's avatar
   
Dries committed
122
  global $id, $edit, $languages, $op, $locale_settings;
Kjartan's avatar
Kjartan committed
123

Dries's avatar
   
Dries committed
124
  if ($op != 'overview' && !$edit && session_is_registered("locale_settings")) {
Kjartan's avatar
Kjartan committed
125
126
    $edit = $locale_settings;
  }
Dries's avatar
   
Dries committed
127
  else {
Kjartan's avatar
Kjartan committed
128
129
    $locale_settings = $edit;
    session_register("locale_settings");
Dries's avatar
   
Dries committed
130
131
  }

132
  if (is_array($edit)) {
Dries's avatar
   
Dries committed
133
134
135

    if ($edit["status"]) {
      switch ($edit["language"]) {
Kjartan's avatar
Kjartan committed
136
137
        case "all":
          foreach ($languages as $key=>$value) {
Dries's avatar
   
Dries committed
138
            $tmp[] = $key . (check_query($edit["status"]) == 1 ? " !=" : " =") ." ''";
Kjartan's avatar
Kjartan committed
139
140
141
142
143
          }
          $query[] = implode(" && ", $tmp);
          break;
        case "any":
          foreach ($languages as $key=>$value) {
Dries's avatar
   
Dries committed
144
            $tmp[] = $key . (check_query($edit["status"]) == 1 ? " !=" : " =") ." ''";
Kjartan's avatar
Kjartan committed
145
146
147
148
          }
          $query[] = "(". implode(" || ", $tmp) .")";
          break;
        default:
Dries's avatar
   
Dries committed
149
          $query[] = check_query($edit["language"]) . (check_query($edit["status"]) == 1 ? " !=" : " =") ." ''";
Kjartan's avatar
Kjartan committed
150
151
      }
    }
Dries's avatar
   
Dries committed
152

Dries's avatar
   
Dries committed
153
154
155
156
157
158
159
160
    if ($edit["module"]) {
      $query[] = "location LIKE '%mod=". (check_query($edit["module"]) != "all" ? check_query($edit["module"]) : "") ."%'";
    }

    if ($edit["string"]) {
      $string_query[] = "string LIKE '%". check_query($edit["string"]) ."%'";
      if ($edit["status"] != 2) {
        if (strlen($edit["language"]) == 2) {
Kjartan's avatar
Kjartan committed
161
          $string_query[] = check_query($edit["language"]) ." LIKE '%". check_query($edit["string"]) ."%'";
Dries's avatar
   
Dries committed
162
163
164
        }
        else {
          foreach ($languages as $key=>$value) {
Kjartan's avatar
Kjartan committed
165
            $string_query[] = check_query($key) ." LIKE '%". check_query($edit["string"]) ."%'";
Dries's avatar
   
Dries committed
166
167
168
          }
        }
      }
Kjartan's avatar
Kjartan committed
169
      $query[] = "(". implode(" || ", $string_query) .")";
Dries's avatar
   
Dries committed
170
171
    }

Kjartan's avatar
Kjartan committed
172
    $result = db_query("SELECT * FROM locales". (count($query) ? " WHERE ". implode(" && ", $query) : "") ." ORDER BY string");
Dries's avatar
   
Dries committed
173

Dries's avatar
   
Dries committed
174
    $header = array(t("string"), (($edit["status"] != 2 && strlen($edit["language"]) == 2) ? t("translated string") : t("languages")), array("data" => t("operations"), "colspan" => "2"));
Kjartan's avatar
Kjartan committed
175
    while ($locale = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
176
      $rows[] = array("$locale->string<br /><small><i>$locale->location</i></small>", array("data" => (($edit["status"] != 2 && strlen($edit["language"]) == 2) ? $locale->$edit["language"] : locale_languages($locale)), "align" => "center"), array("data" => l(t("edit locale"), "admin/locale/edit/$locale->lid"), "nowrap" => "nowrap"), array("data" => l(t("delete locale"), "admin/locale/delete/$locale->lid"), "nowrap" => "nowrap"));
Kjartan's avatar
Kjartan committed
177
    }
Dries's avatar
   
Dries committed
178
    $output .= table($header, $rows);
Kjartan's avatar
Kjartan committed
179
  }
Kjartan's avatar
Kjartan committed
180

Dries's avatar
   
Dries committed
181
182
  reset($languages);

Dries's avatar
   
Dries committed
183
184
185
186
187
  $form .= form_textfield(t("String"), "string", $edit["string"], 30, 30, t("Leave blank to show all strings. This is treated as a regular expression."));
  $form .= form_select(t("Language"), "language", ($edit["language"] ? $edit["language"] : key($languages)), array_merge(array("any" => t("Any language"), "all" => t("All languages")), $languages), t("In which language must the string be translated/untranslated (see status)?"));
  $form .= form_select(t("Status"), "status", $edit["status"], array(2 => t("Untranslated"), 1 => t("Translated"), 0 => t("All")));
  $form .= form_select(t("Module"), "module", $edit["module"], array_merge(array("0" => t("All modules + pages"), "all" => t("All modules")), module_list()));
  $form .= form_submit(t("Search"));
Dries's avatar
   
Dries committed
188

Dries's avatar
   
Dries committed
189
  $output .= form($form);
Kjartan's avatar
Kjartan committed
190
191
192
193

  return $output;
}

Dries's avatar
   
Dries committed
194
function locale_admin() {
Dries's avatar
   
Dries committed
195
  global $op, $edit;
Dries's avatar
   
Dries committed
196

Dries's avatar
   
Dries committed
197
  if (user_access("administer locales")) {
Steven Wittens's avatar
Steven Wittens committed
198
199
    locale_admin_initialize();

Dries's avatar
   
Dries committed
200
201
202
    if (empty($op)) {
      $op = arg(2);
    }
Dries's avatar
   
Dries committed
203
204
205

    switch ($op) {
      case "delete":
Dries's avatar
   
Dries committed
206
        print status(locale_delete(check_query(arg(3))));
Dries's avatar
   
Dries committed
207
        print locale_seek();
Dries's avatar
   
Dries committed
208
209
        break;
      case "edit":
Dries's avatar
   
Dries committed
210
        print locale_edit(check_query(arg(3)));
Dries's avatar
   
Dries committed
211
        break;
212
      case "search":
Dries's avatar
   
Dries committed
213
      case t("Search"):
Dries's avatar
   
Dries committed
214
215
        print locale_seek();
        break;
Dries's avatar
   
Dries committed
216
217
      case t("Save translations"):
        print status(locale_save(check_query(arg(3))));
Dries's avatar
   
Dries committed
218
        print locale_seek();
Kjartan's avatar
Kjartan committed
219
        break;
Dries's avatar
   
Dries committed
220
      default:
Dries's avatar
   
Dries committed
221
222
223
224
225
226
227
228
229
230
        if (arg(3) == "translated") {
          $edit["status"] = 1;
          $edit["language"] = arg(2);
          print locale_seek();
        }
        else {
          $edit["status"] = 2;
          $edit["language"] = arg(2);
          print locale_seek();
        }
Dries's avatar
   
Dries committed
231
232
233
234
    }
  }
  else {
    print message_access();
Dries's avatar
   
Dries committed
235
236
237
  }
}

Steven Wittens's avatar
Steven Wittens committed
238
function locale_admin_initialize() {
Dries's avatar
   
Dries committed
239
240
241
242
243
  /*
  ** This function inserts common strings into the locale table (eg.
  ** names of months and days).  We use $revision and a stored variable
  ** to track if the locale table is up-to-date.
  */
Steven Wittens's avatar
Steven Wittens committed
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262

  $revision = 1;

  if (variable_get("locale_initialize_revision", 0) < $revision) {
    variable_set("locale_initialize_revision", $revision);
    for ($i = 1; $i <= 12; $i++) {
      $stamp = mktime(0, 0, 0, $i, 1, 1971);
      t(date("F", $stamp));
      t(date("M", $stamp));
    }

    for ($i = 0; $i <= 7; $i++) {
      $stamp = $i * 86400;
      t(date("D", $stamp));
      t(date("l", $stamp));
    }
  }
}

Dries's avatar
   
Dries committed
263
264
function locale($string) {
  global $locale;
Dries's avatar
   
Dries committed
265
266
  static $locale_t;

Dries's avatar
   
Dries committed
267
  if (!isset($locale_t)) {
Dries's avatar
   
Dries committed
268
269
270
271
    $cache = cache_get("locale:$locale");
    if ($cache) {
      $locale_t = unserialize($cache->data);
    }
Dries's avatar
   
Dries committed
272
  }
Dries's avatar
   
Dries committed
273

Dries's avatar
   
Dries committed
274
  if ($locale_t[$string] != "") {
Dries's avatar
   
Dries committed
275
    $string = $locale_t[$string];
Dries's avatar
   
Dries committed
276
277
278
279
  }
  else {
    $result = db_query("SELECT lid, $locale FROM locales WHERE string = '%s'", $string);
    if (!db_fetch_object($result)) {
280
      db_query("INSERT INTO locales (string, location) VALUES ('%s', '%s')", $string, request_uri());
Dries's avatar
   
Dries committed
281
    }
Dries's avatar
Dries committed
282
  }
Dries's avatar
   
Dries committed
283

Dries's avatar
   
Dries committed
284
285
  return $string;
}
Dries's avatar
   
Dries committed
286

Dries's avatar
Dries committed
287
?>