1
0

Move the language module from where i was hiding it in contrib to the main repository

This commit is contained in:
Tim Almdal 2010-06-04 14:54:02 -07:00
parent 7ce96db688
commit 5bbb4358c2
17 changed files with 0 additions and 2428 deletions

View File

@ -1,131 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class Admin_Languages_Controller extends Admin_Controller {
public function index($share_translations_form=null) {
$v = new Admin_View("admin.html");
$v->page_title = t("Languages and translations");
$v->content = new View("admin_languages.html");
$v->content->available_locales = locales::available();
$v->content->installed_locales = locales::installed();
$v->content->default_locale = module::get_var("gallery", "default_locale");
if (empty($share_translations_form)) {
$share_translations_form = $this->_share_translations_form();
}
$v->content->share_translations_form = $share_translations_form;
$this->_outgoing_translations_count();
print $v;
}
public function save() {
access::verify_csrf();
$input = Input::instance();
locales::update_installed($input->post("installed_locales"));
$installed_locales = array_keys(locales::installed());
$new_default_locale = $input->post("default_locale");
if (!in_array($new_default_locale, $installed_locales)) {
if (!empty($installed_locales)) {
$new_default_locale = $installed_locales[0];
} else {
$new_default_locale = "en_US";
}
}
module::set_var("gallery", "default_locale", $new_default_locale);
print json_encode(array("result" => "success"));
}
public function share() {
access::verify_csrf();
$form = $this->_share_translations_form();
if (!$form->validate()) {
// Show the page with form errors
return $this->index($form);
}
if (Input::instance()->post("share")) {
l10n_client::submit_translations();
message::success(t("Translations submitted"));
} else {
return $this->_save_api_key($form);
}
url::redirect("admin/languages");
}
private function _save_api_key($form) {
$new_key = $form->sharing->api_key->value;
if ($new_key && !l10n_client::validate_api_key($new_key)) {
$form->sharing->api_key->add_error("invalid", 1);
$valid = false;
} else {
$valid = true;
}
if ($valid) {
$old_key = l10n_client::api_key();
l10n_client::api_key($new_key);
if ($old_key && !$new_key) {
message::success(t("Your API key has been cleared."));
} else if ($old_key && $new_key && $old_key != $new_key) {
message::success(t("Your API key has been changed."));
} else if (!$old_key && $new_key) {
message::success(t("Your API key has been saved."));
} else if ($old_key && $new_key && $old_key == $new_key) {
message::info(t("Your API key was not changed."));
}
log::success(t("gallery"), t("l10n_client API key changed."));
url::redirect("admin/languages");
} else {
// Show the page with form errors
$this->index($form);
}
}
private function _outgoing_translations_count() {
return ORM::factory("outgoing_translation")->count_all();
}
private function _share_translations_form() {
$form = new Forge("admin/languages/share", "", "post", array("id" => "g-share-translations-form"));
$group = $form->group("sharing")
->label(t("Sharing your own translations with the Gallery community is easy. Please do!"));
$api_key = l10n_client::api_key();
$server_link = l10n_client::server_api_key_url();
$group->input("api_key")
->label(empty($api_key)
? t("This is a unique key that will allow you to send translations to the remote
server. To get your API key go to %server-link.",
array("server-link" => html::mark_clean(html::anchor($server_link))))
: t("API key"))
->value($api_key)
->error_messages("invalid", t("The API key you provided is invalid."));
$group->submit("save")->value(t("Save settings"));
if ($api_key && $this->_outgoing_translations_count()) {
// TODO: UI improvement: hide API key / save button when API key is set.
$group->submit("share")->value(t("Submit translations"));
}
return $form;
}
}

View File

@ -1,179 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class L10n_Client_Controller extends Controller {
public function save() {
access::verify_csrf();
if (!identity::active_user()->admin) {
access::forbidden();
}
$locale = Gallery_I18n::instance()->locale();
$input = Input::instance();
$key = $input->post("l10n-message-key");
$root_message = ORM::factory("incoming_translation")
->where("key", "=", $key)
->where("locale", "=", "root")
->find();
if (!$root_message->loaded()) {
throw new Exception("@todo bad request data / illegal state");
}
$is_plural = Gallery_I18n::is_plural_message(unserialize($root_message->message));
$is_empty = true;
if ($is_plural) {
$plural_forms = l10n_client::plural_forms($locale);
$translation = array();
foreach($plural_forms as $plural_form) {
$value = $input->post("l10n-edit-plural-translation-$plural_form");
if (null === $value || !is_string($value)) {
throw new Exception("@todo bad request data");
}
$translation[$plural_form] = $value;
$is_empty = $is_empty && empty($value);
}
} else {
$translation = $input->post("l10n-edit-translation");
$is_empty = empty($translation);
if (null === $translation || !is_string($translation)) {
throw new Exception("@todo bad request data");
}
}
$entry = ORM::factory("outgoing_translation")
->where("key", "=", $key)
->where("locale", "=", $locale)
->find();
if ($is_empty) {
if ($entry->loaded()) {
$entry->delete();
}
} else {
if (!$entry->loaded()) {
$entry->key = $key;
$entry->locale = $locale;
$entry->message = $root_message->message;
$entry->base_revision = null;
}
$entry->translation = serialize($translation);
$entry_from_incoming = ORM::factory("incoming_translation")
->where("key", "=", $key)
->where("locale", "=", $locale)
->find();
if (!$entry_from_incoming->loaded()) {
$entry->base_revision = $entry_from_incoming->revision;
}
$entry->save();
}
Gallery_I18n::clear_cache($locale);
print json_encode(new stdClass());
}
public function toggle_l10n_mode() {
access::verify_csrf();
if (!identity::active_user()->admin) {
access::forbidden();
}
$session = Session::instance();
$l10n_mode = $session->get("l10n_mode", false);
$session->set("l10n_mode", !$l10n_mode);
$redirect_url = "admin/languages";
if (!$l10n_mode) {
$redirect_url .= "#l10n-client";
}
url::redirect($redirect_url);
}
private static function _l10n_client_search_form() {
$form = new Forge("#", "", "post", array("id" => "g-l10n-search-form"));
$group = $form->group("l10n_search");
$group->input("l10n-search")->id("g-l10n-search");
return $form;
}
public static function l10n_form() {
if (Input::instance()->get("show_all_l10n_messages")) {
$calls = array();
foreach (db::build()
->select("key", "message")
->from("incoming_translations")
->where("locale", "=", "root")
->execute() as $row) {
$calls[$row->key] = array(unserialize($row->message), array());
}
} else {
$calls = Gallery_I18n::instance()->call_log();
}
$locale = Gallery_I18n::instance()->locale();
if ($calls) {
$translations = array();
foreach (db::build()
->select("key", "translation")
->from("incoming_translations")
->where("locale", "=", $locale)
->execute() as $row) {
$translations[$row->key] = unserialize($row->translation);
}
// Override incoming with outgoing...
foreach (db::build()
->select("key", "translation")
->from("outgoing_translations")
->where("locale", "=", $locale)
->execute() as $row) {
$translations[$row->key] = unserialize($row->translation);
}
$string_list = array();
$cache = array();
foreach ($calls as $key => $call) {
list ($message, $options) = $call;
// Ensure that the message is in the DB
l10n_scanner::process_message($message, $cache);
// Note: Not interpolating placeholders for the actual translation input field.
// TODO: Might show a preview w/ interpolations (using $options)
$translation = isset($translations[$key]) ? $translations[$key] : '';
$string_list[] = array('source' => $message,
'key' => $key,
'translation' => $translation);
}
$v = new View('l10n_client.html');
$v->string_list = $string_list;
$v->l10n_search_form = self::_l10n_client_search_form();
$v->plural_forms = l10n_client::plural_forms($locale);
return $v;
}
return '';
}
}

View File

@ -1,51 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class language_Controller extends Controller {
public function index() {
print $this->_get_form();
}
public function handler() {
access::verify_csrf();
$form = $this->_get_form();
if ($form->validate()) {
// @todo process the admin form
message::success(t("language Processing Successfully"));
print json_encode(
array("result" => "success"));
} else {
print json_encode(
array("result" => "error",
"form" => $form->__toString()));
}
}
private function _get_form() {
$form = new Forge("language/handler", "", "post",
array("id" => "g-language-form"));
$group = $form->group("group")->label(t("language Handler"));
$group->input("text")->label(t("Text"))->rules("required");
$group->submit("submit")->value(t("Submit"));
return $form;
}
}

View File

@ -1,206 +0,0 @@
/**
* TODO(andy_st): Add original copyright notice from Drupal l10_client.
* TODO(andy_st): Add G3 copyright notice.
* TODO(andy_st): clean up formatting to match our other CSS files.
*/
/* $Id: l10n_client.css,v 1.6 2008/09/09 10:48:20 goba Exp $ */
/* width percentages add to 99% rather than 100% to prevent float
overflows from occurring in an unnamed browser that can't decide
how it wants to round. */
/* l10n_client container */
#l10n-client {
text-align:left;
z-index:99;
line-height:1em;
color:#000; background:#fff;
position:fixed;
width:100%; height: 2em;
bottom:0px; left:0px;
overflow:hidden;}
* html #l10n-client {
position:static;}
#l10n-client-string-select .string-list,
#l10n-client-string-editor .source,
#l10n-client-string-editor .editor {
height:20em;}
#l10n-client .labels {
overflow:hidden;
position:relative;
height:2em;
color:#fff;
background:#37a;}
#l10n-client .labels .label {
display:none;}
/* Panel toggle button (span) */
#l10n-client-toggler {
cursor:pointer;
display:block;
position:absolute; right:0em;
height:2em; line-height:2em;
text-align:center; background:#000;
}
#l10n-client-toggler a {
font-size: 1em;
padding: .5em;
}
#l10n-client-toggler #g-minimize-l10n {
border-right: 1px solid #ffffff;
}
/* Panel labels */
#l10n-client h2 {
border-left:1px solid #fff;
height:1em; line-height:1em;
padding: .5em; margin:0px;
font-size:1em;
}
#l10n-client .strings h2 {
border:0px;}
/* 25 + 37 + 37 = 99 */
#l10n-client .strings {
width:25%; float:left;}
#l10n-client .source {
width:37%; float:left;}
#l10n-client .translation {
width:37%; float:left;}
/* Translatable string list */
#l10n-client-string-select {
display:none;
float:left;
width:25%;
direction: ltr;
}
#l10n-client .string-list {
height: 16em;
overflow:auto;
list-style:none; list-style-image:none;
margin:0em; padding:0em;}
#l10n-client .string-list li {
font-size:.9em;
line-height:1.5em;
cursor:default;
background:transparent;
list-style:none; list-style-image:none;
border-bottom:1px solid #ddd;
padding:.25em .5em;
margin:0em;}
/* Green for translated */
#l10n-client .string-list li.translated {
border-bottom-color:#9c3;
background:#cf6; color:#360;}
#l10n-client .string-list li.translated:hover {
background: #df8;}
#l10n-client .string-list li.translated:active {
background: #9c3;}
#l10n-client .string-list li.hidden {
display:none;}
/* Gray + Blue hover for untranslated */
#l10n-client .string-list li.untranslated {}
#l10n-client .string-list li.untranslated:hover {
background: #ace;}
#l10n-client .string-list li.untranslated:active {
background: #8ac;}
/* Selected string is indicated by bold text */
#l10n-client .string-list li.active {
font-weight:bold;}
#l10n-client #g-l10n-search-form {
background: #eee;
margin: 0em;
padding: .25em .25em;
}
#l10n-client #g-l10n-search-form .form-item,
#l10n-client #g-l10n-search-form input.form-text,
#l10n-client #g-l10n-search-form #search-filter-go,
#l10n-client #g-l10n-search-form #search-filter-clear {
display:inline;
vertical-align:middle;
}
#l10n-client #g-l10n-search-form .form-item {
margin:0em;
padding:0em;
}
#l10n-client #g-l10n-search-form fieldset {
margin-bottom: 0;
padding: .25em .25em;
}
#l10n-client #g-l10n-search-form input {
width: 96.75%;
}
#l10n-client #g-l10n-search-form #search-filter-clear {
width:10%;
margin:0em;
}
#l10n-client-string-editor {
display:none;
float:left;
width:74%;}
#l10n-client-string-editor .source {
overflow:hidden;
width:50%;
float:left;
}
#l10n-client-string-editor .source .source-text {
line-height:1.5em;
background:#eee;
font-family: monospace;
text-align: left;
height:16em; margin:1em; padding:1em;
overflow:auto;
direction: ltr;
}
#l10n-client-string-editor .translation {
overflow-y:auto;
overflow-x: hidden;
height: 20em;
width:49%;
float: right;
}
#g-l10n-client-save-form {
padding: 0em;
}
#l10n-client form ul,
#l10n-client form li,
#l10n-client form input[type=submit],
#l10n-client form input[type=text] {
display: inline ! important ;
}
#l10n-client form .hidden {
display: none;
}

View File

@ -1,312 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class l10n_client_Core {
private static function _server_url() {
return "http://gallery.menalto.com/index.php";
}
static function server_api_key_url() {
return self::_server_url() . "?q=translations/userkey/" .
self::client_token();
}
static function client_token() {
return md5("l10n_client_client_token" . access::private_key());
}
static function api_key($api_key=null) {
if ($api_key !== null) {
module::set_var("gallery", "l10n_client_key", $api_key);
}
return module::get_var("gallery", "l10n_client_key", "");
}
static function server_uid($api_key=null) {
$api_key = $api_key == null ? self::api_key() : $api_key;
$parts = explode(":", $api_key);
return empty($parts) ? 0 : $parts[0];
}
private static function _sign($payload, $api_key=null) {
$api_key = $api_key == null ? self::api_key() : $api_key;
return md5($api_key . $payload . self::client_token());
}
static function validate_api_key($api_key) {
$version = "1.0";
$url = self::_server_url() . "?q=translations/status";
$signature = self::_sign($version, $api_key);
list ($response_data, $response_status) = remote::post(
$url, array("version" => $version,
"client_token" => self::client_token(),
"signature" => $signature,
"uid" => self::server_uid($api_key)));
if (!remote::success($response_status)) {
return false;
}
return true;
}
/**
* Fetches translations for l10n messages. Must be called repeatedly
* until 0 is returned (which is a countdown indicating progress).
*
* @param $num_fetched in/out parameter to specify which batch of
* messages to fetch translations for.
* @return The number of messages for which we didn't fetch
* translations for.
*/
static function fetch_updates(&$num_fetched) {
$request = new stdClass();
$request->locales = array();
$request->messages = new stdClass();
$locales = locales::installed();
foreach ($locales as $locale => $locale_data) {
$request->locales[] = $locale;
}
// See the server side code for how we arrive at this
// number as a good limit for #locales * #messages.
$max_messages = 2000 / count($locales);
$num_messages = 0;
$rows = db::build()
->select("key", "locale", "revision", "translation")
->from("incoming_translations")
->order_by("key")
->limit(1000000) // ignore, just there to satisfy SQL syntax
->offset($num_fetched)
->execute();
$num_remaining = $rows->count();
foreach ($rows as $row) {
if (!isset($request->messages->{$row->key})) {
if ($num_messages >= $max_messages) {
break;
}
$request->messages->{$row->key} = 1;
$num_messages++;
}
if (!empty($row->revision) && !empty($row->translation) &&
isset($locales[$row->locale])) {
if (!is_object($request->messages->{$row->key})) {
$request->messages->{$row->key} = new stdClass();
}
$request->messages->{$row->key}->{$row->locale} = (int) $row->revision;
}
$num_fetched++;
$num_remaining--;
}
// @todo Include messages from outgoing_translations?
if (!$num_messages) {
return $num_remaining;
}
$request_data = json_encode($request);
$url = self::_server_url() . "?q=translations/fetch";
list ($response_data, $response_status) = remote::post($url, array("data" => $request_data));
if (!remote::success($response_status)) {
throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED " . $response_status);
}
if (empty($response_data)) {
return $num_remaining;
}
$response = json_decode($response_data);
// Response format (JSON payload):
// [{key:<key_1>, translation: <JSON encoded translation>, rev:<rev>, locale:<locale>},
// {key:<key_2>, ...}
// ]
foreach ($response as $message_data) {
// @todo Better input validation
if (empty($message_data->key) || empty($message_data->translation) ||
empty($message_data->locale) || empty($message_data->rev)) {
throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED: Invalid response data");
}
$key = $message_data->key;
$locale = $message_data->locale;
$revision = $message_data->rev;
$translation = json_decode($message_data->translation);
if (!is_string($translation)) {
// Normalize stdclass to array
$translation = (array) $translation;
}
$translation = serialize($translation);
// @todo Should we normalize the incoming_translations table into messages(id, key, message)
// and incoming_translations(id, translation, locale, revision)? Or just allow
// incoming_translations.message to be NULL?
$locale = $message_data->locale;
$entry = ORM::factory("incoming_translation")
->where("key", "=", $key)
->where("locale", "=", $locale)
->find();
if (!$entry->loaded()) {
// @todo Load a message key -> message (text) dict into memory outside of this loop
$root_entry = ORM::factory("incoming_translation")
->where("key", "=", $key)
->where("locale", "=", "root")
->find();
$entry->key = $key;
$entry->message = $root_entry->message;
$entry->locale = $locale;
}
$entry->revision = $revision;
$entry->translation = $translation;
$entry->save();
}
return $num_remaining;
}
static function submit_translations() {
// Request format (HTTP POST):
// client_token = <client_token>
// uid = <l10n server user id>
// signature = md5(user_api_key($uid, $client_token) . $data . $client_token))
// data = // JSON payload
//
// {<key_1>: {message: <JSON encoded message>
// translations: {<locale_1>: <JSON encoded translation>,
// <locale_2>: ...}},
// <key_2>: {...}
// }
// @todo Batch requests (max request size)
// @todo include base_revision in submission / how to handle resubmissions / edit fights?
foreach (db::build()
->select("key", "message", "locale", "base_revision", "translation")
->from("outgoing_translations")
->execute() as $row) {
$key = $row->key;
if (!isset($request->{$key})) {
$request->{$key}->message = json_encode(unserialize($row->message));
}
$request->{$key}->translations->{$row->locale} = json_encode(unserialize($row->translation));
}
// @todo reduce memory consumption, e.g. free $request
$request_data = json_encode($request);
$url = self::_server_url() . "?q=translations/submit";
$signature = self::_sign($request_data);
list ($response_data, $response_status) = remote::post(
$url, array("data" => $request_data,
"client_token" => self::client_token(),
"signature" => $signature,
"uid" => self::server_uid()));
if (!remote::success($response_status)) {
throw new Exception("@todo TRANSLATIONS_SUBMISSION_FAILED " . $response_status);
}
if (empty($response_data)) {
return;
}
$response = json_decode($response_data);
// Response format (JSON payload):
// [{key:<key_1>, locale:<locale_1>, rev:<rev_1>, status:<rejected|accepted|pending>},
// {key:<key_2>, ...}
// ]
// @todo Move messages out of outgoing into incoming, using new rev?
// @todo show which messages have been rejected / are pending?
}
/**
* Plural forms.
*/
static function plural_forms($locale) {
$parts = explode('_', $locale);
$language = $parts[0];
// Data from CLDR 1.6 (http://unicode.org/cldr/data/common/supplemental/plurals.xml).
// Docs: http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html
switch ($language) {
case 'az':
case 'fa':
case 'hu':
case 'ja':
case 'ko':
case 'my':
case 'to':
case 'tr':
case 'vi':
case 'yo':
case 'zh':
case 'bo':
case 'dz':
case 'id':
case 'jv':
case 'ka':
case 'km':
case 'kn':
case 'ms':
case 'th':
return array('other');
case 'ar':
return array('zero', 'one', 'two', 'few', 'many', 'other');
case 'lv':
return array('zero', 'one', 'other');
case 'ga':
case 'se':
case 'sma':
case 'smi':
case 'smj':
case 'smn':
case 'sms':
return array('one', 'two', 'other');
case 'ro':
case 'mo':
case 'lt':
case 'cs':
case 'sk':
case 'pl':
return array('one', 'few', 'other');
case 'hr':
case 'ru':
case 'sr':
case 'uk':
case 'be':
case 'bs':
case 'sh':
case 'mt':
return array('one', 'few', 'many', 'other');
case 'sl':
return array('one', 'two', 'few', 'other');
case 'cy':
return array('one', 'two', 'many', 'other');
default: // en, de, etc.
return array('one', 'other');
}
}
}

View File

@ -1,153 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* Scans all source code for messages that need to be localized.
*/
class l10n_scanner_Core {
// Based on Drupal's potx module, originally written by:
// G‡bor Hojtsy http://drupal.org/user/4166
public static $cache;
static function process_message($message, &$cache) {
if (empty($cache)) {
foreach (db::build()
->select("key")
->from("incoming_translations")
->where("locale", "=", "root")
->execute() as $row) {
$cache[$row->key] = true;
}
}
$key = Gallery_I18n::get_message_key($message);
if (array_key_exists($key, $cache)) {
return $cache[$key];
}
$entry = ORM::factory("incoming_translation")->where("key", "=", $key)->find();
if (!$entry->loaded()) {
$entry->key = $key;
$entry->message = serialize($message);
$entry->locale = "root";
$entry->save();
}
}
static function scan_php_file($file, &$cache) {
$code = file_get_contents($file);
$raw_tokens = token_get_all($code);
unset($code);
$tokens = array();
$func_token_list = array("t" => array(), "t2" => array());
$token_number = 0;
// Filter out HTML / whitespace, and build a lookup for global function calls.
foreach ($raw_tokens as $token) {
if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) {
if (is_array($token)) {
if ($token[0] == T_STRING && in_array($token[1], array("t", "t2"))) {
$func_token_list[$token[1]][] = $token_number;
}
}
$tokens[] = $token;
$token_number++;
}
}
unset($raw_tokens);
if (!empty($func_token_list["t"])) {
l10n_scanner::_parse_t_calls($tokens, $func_token_list["t"], $cache);
}
if (!empty($func_token_list["t2"])) {
l10n_scanner::_parse_plural_calls($tokens, $func_token_list["t2"], $cache);
}
}
static function scan_info_file($file, &$cache) {
$info = new ArrayObject(parse_ini_file($file), ArrayObject::ARRAY_AS_PROPS);
foreach (array('name', 'description') as $property) {
if (isset($info->$property)) {
l10n_scanner::process_message($info->$property, $cache);
}
}
}
private static function _parse_t_calls(&$tokens, &$call_list, &$cache) {
foreach ($call_list as $index) {
$function_name = $tokens[$index++];
$parens = $tokens[$index++];
$first_param = $tokens[$index++];
$next_token = $tokens[$index];
if ($parens == "(") {
if (in_array($next_token, array(")", ","))
&& (is_array($first_param) && ($first_param[0] == T_CONSTANT_ENCAPSED_STRING))) {
$message = self::_escape_quoted_string($first_param[1]);
l10n_scanner::process_message($message, $cache);
} else {
// t() found, but inside is something which is not a string literal.
// @todo Call status callback with error filename/line.
}
}
}
}
private static function _parse_plural_calls(&$tokens, &$call_list, &$cache) {
foreach ($call_list as $index) {
$function_name = $tokens[$index++];
$parens = $tokens[$index++];
$first_param = $tokens[$index++];
$first_separator = $tokens[$index++];
$second_param = $tokens[$index++];
$next_token = $tokens[$index];
if ($parens == "(") {
if ($first_separator == "," && $next_token == ","
&& is_array($first_param) && $first_param[0] == T_CONSTANT_ENCAPSED_STRING
&& is_array($second_param) && $second_param[0] == T_CONSTANT_ENCAPSED_STRING) {
$singular = self::_escape_quoted_string($first_param[1]);
$plural = self::_escape_quoted_string($second_param[1]);
l10n_scanner::process_message(array("one" => $singular, "other" => $plural), $cache);
} else {
// t2() found, but inside is something which is not a string literal.
// @todo Call status callback with error filename/line.
}
}
}
}
/**
* Escape quotes in a strings depending on the surrounding
* quote type used.
*
* @param $str The strings to escape
*/
private static function _escape_quoted_string($str) {
$quo = substr($str, 0, 1);
$str = substr($str, 1, -1);
if ($quo == '"') {
$str = stripcslashes($str);
} else {
$str = strtr($str, array("\\'" => "'", "\\\\" => "\\"));
}
return $str;
}
}

View File

@ -1,45 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class language_block {
static function get_site_list() {
return array("language" => t("Language preference"));
}
static function get($block_id, $theme) {
$block = new Block();
if ($block_id == "language") {
$locales = locales::installed();
if (count($locales) > 1) {
foreach ($locales as $locale => $display_name) {
$locales[$locale] = SafeString::of_safe_html($display_name);
}
$block = new Block();
$block->css_id = "g-user-language-block";
$block->title = t("Language preference");
$block->content = new View("user_languages_block.html");
$block->content->installed_locales = array_merge(array("" => t("« none »")), $locales);
$block->content->selected = (string) locales::cookie_locale();
} else {
$block = "";
}
}
return $block;
}
}

View File

@ -1,30 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class language_event_Core {
static function admin_menu($menu, $theme) {
$menu->get("settings_menu")
->append(Menu::factory("link")
->id("languages")
->label(t("Languages"))
->url(url::site("admin/languages")));
return $menu;
}
}

View File

@ -1,28 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class language_installer {
static function activate() {
module::set_var("gallery", "language_driver", "multi");
}
static function deactivate() {
module::set_var("gallery", "language_driver", "basic");
module::set_var("gallery", "installed_locales", "en_US");
}
}

View File

@ -1,141 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class gallery_task_Core {
const MPTT_LEFT = 0;
const MPTT_RIGHT = 1;
static function available_tasks() {
$tasks = array();
$tasks[] = Task_Definition::factory()
->callback("gallery_task::update_l10n")
->name(t("Update translations"))
->description(t("Download new and updated translated strings"))
->severity(log::SUCCESS);
return $tasks;
}
static function update_l10n($task) {
$errors = array();
try {
$start = microtime(true);
$data = Cache::instance()->get("update_l10n_cache:{$task->id}");
if ($data) {
list($dirs, $files, $cache, $num_fetched) = unserialize($data);
}
$i = 0;
switch ($task->get("mode", "init")) {
case "init": // 0%
$dirs = array("gallery", "modules", "themes", "installer");
$files = $cache = array();
$num_fetched = 0;
$task->set("mode", "find_files");
$task->status = t("Finding files");
break;
case "find_files": // 0% - 10%
while (($dir = array_pop($dirs)) && microtime(true) - $start < 0.5) {
if (in_array(basename($dir), array("tests", "lib"))) {
continue;
}
foreach (glob(DOCROOT . "$dir/*") as $path) {
$relative_path = str_replace(DOCROOT, "", $path);
if (is_dir($path)) {
$dirs[] = $relative_path;
} else {
$files[] = $relative_path;
}
}
}
$task->status = t2("Finding files: found 1 file",
"Finding files: found %count files", count($files));
if (!$dirs) {
$task->set("mode", "scan_files");
$task->set("total_files", count($files));
$task->status = t("Scanning files");
$task->percent_complete = 10;
}
break;
case "scan_files": // 10% - 70%
while (($file = array_pop($files)) && microtime(true) - $start < 0.5) {
$file = DOCROOT . $file;
switch (pathinfo($file, PATHINFO_EXTENSION)) {
case "php":
l10n_scanner::scan_php_file($file, $cache);
break;
case "info":
l10n_scanner::scan_info_file($file, $cache);
break;
}
}
$total_files = $task->get("total_files");
$task->status = t2("Scanning files: scanned 1 file",
"Scanning files: scanned %count files", $total_files - count($files));
$task->percent_complete = 10 + 60 * ($total_files - count($files)) / $total_files;
if (empty($files)) {
$task->set("mode", "fetch_updates");
$task->status = t("Fetching updates");
$task->percent_complete = 70;
}
break;
case "fetch_updates": // 70% - 100%
// Send fetch requests in batches until we're done
$num_remaining = l10n_client::fetch_updates($num_fetched);
if ($num_remaining) {
$total = $num_fetched + $num_remaining;
$task->percent_complete = 70 + 30 * ((float) $num_fetched / $total);
} else {
Gallery_I18n::clear_cache();
$task->done = true;
$task->state = "success";
$task->status = t("Translations installed/updated");
$task->percent_complete = 100;
}
}
if (!$task->done) {
Cache::instance()->set("update_l10n_cache:{$task->id}",
serialize(array($dirs, $files, $cache, $num_fetched)));
} else {
Cache::instance()->delete("update_l10n_cache:{$task->id}");
}
} catch (Exception $e) {
Kohana_Log::add("error",(string)$e);
$task->done = true;
$task->state = "error";
$task->status = $e->getMessage();
$errors[] = (string)$e;
}
if ($errors) {
$task->log($errors);
}
}
}

View File

@ -1,64 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class language_theme {
static function head($theme) {
$session = Session::instance();
if (count(locales::installed())) {
// Needed by the languages block
$theme->script("jquery.cookie.js");
}
if ($session->get("l10n_mode", false)) {
$theme->css("l10n_client.css");
$theme->script("jquery.cookie.js");
$theme->script("l10n_client.js");
}
return "";
}
static function admin_head($theme) {
$session = Session::instance();
if ($session->get("l10n_mode", false)) {
$theme->css("l10n_client.css");
$theme->script("jquery.cookie.js");
$theme->script("l10n_client.js");
}
}
static function page_bottom($theme) {
$session = Session::instance();
if ($session->get("l10n_mode", false)) {
return L10n_Client_Controller::l10n_form();
}
}
static function admin_page_bottom($theme) {
$session = Session::instance();
if ($session->get("l10n_mode", false)) {
return L10n_Client_Controller::l10n_form();
}
}
static function body_attributes() {
if (locales::is_rtl()) {
return 'class="rtl"';
}
}
}

View File

@ -1,315 +0,0 @@
// Fork from Drupal's l10n_client module, originally written by:
// G‡bor Hojtsy http://drupal.org/user/4166 (original author)
// Young Hahn / Development Seed - http://developmentseed.org/ (friendly user interface)
var Gallery = Gallery || { 'behaviors': {} };
Gallery.attachBehaviors = function(context) {
context = context || document;
// Execute all of them.
jQuery.each(Gallery.behaviors,
function() {
this(context);
});
};
$(document).ready(function() {
Gallery.attachBehaviors(this);
});
// Store all l10n_client related data + methods in its own object
jQuery.extend(Gallery, {
l10nClient: new (function() {
// Set "selected" string to unselected, i.e. -1
this.selected = -1;
// Keybindings
this.keys = {'toggle':'ctrl+shift+s', 'clear': 'esc'}; // Keybindings
// Keybinding functions
this.key = function(pressed) {
switch(pressed) {
case 'toggle':
// Grab user-hilighted text & send it into the search filter
userSelection = window.getSelection ? window.getSelection() : document.getSelection ? document.getSelection() : document.selection.createRange().text;
userSelection = String(userSelection);
if(userSelection.length > 0) {
Gallery.l10nClient.filter(userSelection);
Gallery.l10nClient.toggle(1);
$('#l10n-client #g-l10n-search').focus();
} else {
if($('#l10n-client').is('.hidden')) {
Gallery.l10nClient.toggle(1);
if(!$.browser.safari) {
$('#l10n-client #g-l10n-search').focus();
}
} else {
Gallery.l10nClient.toggle(0);
}
}
break;
case 'clear':
this.filter(false);
break;
}
};
// Toggle the l10nclient
this.toggle = function(state) {
switch(state) {
case 1:
$('#l10n-client-string-select, #l10n-client-string-editor, #l10n-client .labels .label').show();
$('#l10n-client').height('22em').removeClass('hidden');
//$('#l10n-client').slideUp();
$('#g-minimize-l10n').text("_");
/*
* This CSS clashes with Gallery's CSS, probably due to
* YUI's grid / floats.
if(!$.browser.msie) {
$('body').css('border-bottom', '22em solid #fff');
}
*/
$.cookie('Gallery_l10n_client', '1', {expires: 7, path: '/'});
break;
case 0:
$('#l10n-client-string-select, #l10n-client-string-editor, #l10n-client .labels .label').hide();
$('#l10n-client').height('2em').addClass('hidden');
// TODO: Localize this message
$('#g-minimize-l10n').text(MSG_TRANSLATE_TEXT);
/*
if(!$.browser.msie) {
$('body').css('border-bottom', '0px');
}
*/
$.cookie('Gallery_l10n_client', '0', {expires: 7, path: '/'});
break;
}
};
// Get a string from the DOM tree
this.getString = function(index, type) {
if (index < l10n_client_data.length) {
return l10n_client_data[index][type];
}
return "";
};
// Set a string in the DOM tree
this.setString = function(index, data) {
l10n_client_data[index]['translation'] = data;
};
// Display the source message
this.showSourceMessage = function(source, is_plural) {
if (is_plural) {
var pretty_source = $('#source-text-tmp-space').text('[one] - ' + source['one']).html();
pretty_source += '<br/>';
pretty_source += $('#source-text-tmp-space').text('[other] - ' + source['other']).html();
} else {
var pretty_source = $('#source-text-tmp-space').text(source).html();
}
$('#l10n-client-string-editor .source-text').html(pretty_source);
};
this.isPluralMessage = function(message) {
return typeof(message) == 'object';
};
this.updateTranslationForm = function(translation, is_plural) {
$('.translationField').addClass('hidden');
if (is_plural) {
if (typeof(translation) != 'object') {
translation = {};
}
var num_plural_forms = plural_forms.length;
for (var i = 0; i < num_plural_forms; i++) {
var form = plural_forms[i];
if (translation[form] == undefined) {
translation[form] = '';
}
$("#plural-" + form + " textarea[name='l10n-edit-plural-translation-" + form + "']")
.attr('value', translation[form]);
$('#plural-' + form).removeClass('hidden');
}
} else {
$('#l10n-edit-translation').attr('value', translation);
$('#l10n-edit-translation').removeClass('hidden');
}
};
// Filter the string list by a search string
this.filter = function(search) {
if(search == false || search == '') {
$('#l10n-client #l10n-search-filter-clear').focus();
$('#l10n-client-string-select li').show();
$('#l10n-client #g-l10n-search').val('');
$('#l10n-client #g-l10n-search').focus();
} else {
if(search.length > 0) {
$('#l10n-client-string-select li').hide();
$('#l10n-client-string-select li:contains('+search+')').show();
$('#l10n-client #g-l10n-search').val(search);
}
}
};
this.copySourceText = function() {
var index = Gallery.l10nClient.selected;
if (index >= 0) {
var source = Gallery.l10nClient.getString(index, 'source');
var is_plural = Gallery.l10nClient.isPluralMessage(source);
if (is_plural) {
if (typeof(translation) != 'object') {
translation = {};
}
var num_plural_forms = plural_forms.length;
for (var i = 0; i < num_plural_forms; i++) {
var form = plural_forms[i];
var text = source['other'];
if (form == 'one') {
text = source['one'];
}
$("#plural-" + form + " textarea[name='l10n-edit-plural-translation-" + form + "']")
.attr('value', text);
}
} else {
$('#l10n-edit-translation').attr('value', source);
}
}
};
})
});
// Attaches the localization editor behavior to all required fields.
Gallery.behaviors.l10nClient = function(context) {
switch($.cookie('Gallery_l10n_client')) {
case '1':
Gallery.l10nClient.toggle(1);
break;
default:
Gallery.l10nClient.toggle(0);
break;
}
// If the selection changes, copy string values to the source and target fields.
// Add class to indicate selected string in list widget.
$('#l10n-client-string-select li').click(function() {
$('#l10n-client-string-select li').removeClass('active');
$(this).addClass('active');
var index = $('#l10n-client-string-select li').index(this);
var source = Gallery.l10nClient.getString(index, 'source');
var key = Gallery.l10nClient.getString(index, 'key');
var is_plural = Gallery.l10nClient.isPluralMessage(source);
Gallery.l10nClient.showSourceMessage(source, is_plural);
Gallery.l10nClient.updateTranslationForm(Gallery.l10nClient.getString(index, 'translation'), is_plural);
$("#g-l10n-client-save-form input[name='l10n-message-key']").val(key);
Gallery.l10nClient.selected = index;
});
// When l10n_client window is clicked, toggle based on current state.
$('#g-minimize-l10n').click(function() {
if($('#l10n-client').is('.hidden')) {
Gallery.l10nClient.toggle(1);
} else {
Gallery.l10nClient.toggle(0);
}
});
// Close the l10n client using an AJAX call and refreshing the page
$('#g-close-l10n').click(function(event) {
$.ajax({
type: "GET",
url: toggle_l10n_mode_url,
data: "csrf=" + csrf,
success: function() {
window.location.reload(true);
}
});
event.preventDefault();
});
// Register keybindings using jQuery hotkeys
// TODO: Either remove hotkeys code or add query.hotkeys.js.
if($.hotkeys) {
$.hotkeys.add(Gallery.l10nClient.keys['toggle'], function(){Gallery.l10nClient.key('toggle');});
$.hotkeys.add(Gallery.l10nClient.keys['clear'], {target:'#l10n-client #g-l10n-search', type:'keyup'}, function(){Gallery.l10nClient.key('clear');});
}
// never actually submit the form as the search is done in the browser
$('#g-l10n-search-form').submit(function() {
return false;
});
// Custom listener for l10n_client livesearch
$('#l10n-client #g-l10n-search').keyup(function(key) {
Gallery.l10nClient.filter($('#l10n-client #g-l10n-search').val());
});
// Clear search
$('#l10n-client #l10n-search-filter-clear').click(function() {
Gallery.l10nClient.filter(false);
return false;
});
// Send AJAX POST data on form submit.
$('#g-l10n-client-save-form').ajaxForm({
dataType: "json",
success: function(data) {
var source = Gallery.l10nClient.getString(Gallery.l10nClient.selected, 'source');
var is_plural = Gallery.l10nClient.isPluralMessage(source);
var num_plural_forms = plural_forms.length;
// Store translation in local js
var translation = {};
var is_non_empty = false;
if (is_plural) {
for (var i = 0; i < num_plural_forms; i++) {
var form = plural_forms[i];
translation[form] = $("#plural-" + form + " textarea[name='l10n-edit-plural-translation-" + form + "']").attr('value');
is_non_empty = is_non_empty || translation[form];
}
} else {
translation = $('#l10n-edit-translation').attr('value');
is_non_empty = translation;
}
Gallery.l10nClient.setString(Gallery.l10nClient.selected, translation);
// Mark message as translated / untranslated.
var source_element = $('#l10n-client-string-select li').eq(Gallery.l10nClient.selected);
if (is_non_empty) {
source_element.removeClass('untranslated').removeClass('active').addClass('translated');
} else {
source_element.removeClass('active').removeClass('translated').addClass('untranslated');
}
// Clear the translation form fields
Gallery.l10nClient.showSourceMessage('', false);
$('#g-l10n-client-save-form #l10n-edit-translation').val('');
for (var i = 0; i < num_plural_forms; i++) {
var form = plural_forms[i];
$("#plural-" + form + " textarea[name='l10n-edit-plural-translation-" + form + "']").val('');
}
$("#g-l10n-client-save-form input[name='l10n-message-key']").val('');
},
error: function(xmlhttp) {
// TODO: Localize this message
alert('An HTTP error @status occured (or empty response).'.replace('@status', xmlhttp.status));
}
});
// TODO: Add copy/clear buttons (without ajax behavior)
/* <input type="submit" name="l10n-edit-copy" value="<?= t("Copy source") ?>"/>
<input type="submit" name="l10n-edit-clear" value="<?= t("Clear") ?>"/>
*/
// TODO: Handle plurals in copy button
// Copy source text to translation field on button click.
$('#g-l10n-client-save-form #l10n-edit-copy').click(function() {
$('#g-l10n-client-save-form #l10n-edit-target').val($('#l10n-client-string-editor .source-text').text());
});
// Clear translation field on button click.
$('#g-l10n-client-save-form #l10n-edit-clear').click(function() {
$('#g-l10n-client-save-form #l10n-edit-target').val('');
});
};

View File

@ -1,553 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2010 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* Provides a driver-based interface for internationaization and localization.
*/
class Language_Multi_Driver extends Language_Driver {
public function locale($locale=null) {
if ($locale) {
$this->_config['default_locale'] = $locale;
$php_locale = setlocale(LC_ALL, 0);
list ($php_locale, $unused) = explode('.', $php_locale . '.');
if ($php_locale != $locale) {
// Attempt to set PHP's locale as well (for number formatting, collation, etc.)
$locale_prefs = array($locale);
// Try appending some character set names; some systems (like FreeBSD) need this.
// Some systems require a format with hyphen (eg. Gentoo) and others without (eg. FreeBSD).
$charsets = array('utf8', 'UTF-8', 'UTF8', 'ISO8859-1', 'ISO-8859-1');
if (substr($locale, 0, 2) != 'en') {
$charsets = array_merge($charsets, array(
'EUC', 'Big5', 'euc', 'ISO8859-2', 'ISO8859-5', 'ISO8859-7',
'ISO8859-9', 'ISO-8859-2', 'ISO-8859-5', 'ISO-8859-7', 'ISO-8859-9'));
}
foreach ($charsets as $charset) {
$locale_prefs[] = $locale . '.' . $charset;
}
$locale_prefs[] = 'en_US';
$php_locale = setlocale(LC_ALL, $locale_prefs);
}
if (is_string($php_locale) && substr($php_locale, 0, 2) == 'tr') {
// Make PHP 5 work with Turkish (the localization results are mixed though).
// Hack for http://bugs.php.net/18556
setlocale(LC_CTYPE, 'C');
}
}
return $this->_config['default_locale'];
}
public function is_rtl($locale=null) {
$is_rtl = !empty($this->_config["force_rtl"]);
if (empty($is_rtl)) {
$locale or $locale = $this->locale();
list ($language, $territory) = explode('_', $locale . "_");
$is_rtl = in_array($language, array("he", "fa", "ar"));
}
return $is_rtl;
}
/**
* @see Language_Driver::translate
*/
public function translate($message, $options=array()) {
$locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale'];
$count = isset($options['count']) ? $options['count'] : null;
$values = $options;
unset($values['locale']);
$this->log($message, $options);
$entry = $this->lookup($locale, $message);
if (null === $entry) {
// Default to the root locale.
$entry = $message;
$locale = $this->_config['root_locale'];
}
$entry = $this->pluralize($locale, $entry, $count);
$entry = $this->interpolate($locale, $entry, $values);
return SafeString::of_safe_html($entry);
}
public function has_translation($message, $options=null) {
$locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale'];
$entry = $this->lookup($locale, $message);
if (null === $entry) {
return false;
} else if (!is_array($message)) {
return $entry !== '';
} else {
if (!is_array($entry) || empty($entry)) {
return false;
}
// It would be better to verify that all the locale's plural forms have a non-empty
// translation, but this is fine for now.
foreach ($entry as $value) {
if ($value === '') {
return false;
}
}
return true;
}
}
private function lookup($locale, $message) {
if (!isset($this->_cache[$locale])) {
$this->_cache[$locale] = $this->_load_translations($locale);
}
$key = $this->get_message_key($message);
if (isset($this->_cache[$locale][$key])) {
return $this->_cache[$locale][$key];
} else {
return null;
}
}
private function _load_translations($locale) {
$cache_key = "translation|" . $locale;
$cache = Cache::instance();
$translations = $cache->get($cache_key);
if (!isset($translations) || !is_array($translations)) {
$translations = array();
foreach (db::build()
->select("key", "translation")
->from("incoming_translations")
->where("locale", "=", $locale)
->execute() as $row) {
$translations[$row->key] = unserialize($row->translation);
}
// Override incoming with outgoing...
foreach (db::build()
->select("key", "translation")
->from("outgoing_translations")
->where("locale", "=", $locale)
->execute() as $row) {
$translations[$row->key] = unserialize($row->translation);
}
$cache->set($cache_key, $translations, array("translation"), 0);
}
return $translations;
}
protected function _get_plural_key($locale, $count) {
$parts = explode('_', $locale);
$language = $parts[0];
// Data from CLDR 1.6 (http://unicode.org/cldr/data/common/supplemental/plurals.xml).
// Docs: http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html
switch ($language) {
case 'az':
case 'fa':
case 'hu':
case 'ja':
case 'ko':
case 'my':
case 'to':
case 'tr':
case 'vi':
case 'yo':
case 'zh':
case 'bo':
case 'dz':
case 'id':
case 'jv':
case 'ka':
case 'km':
case 'kn':
case 'ms':
case 'th':
return 'other';
case 'ar':
if ($count == 0) {
return 'zero';
} else if ($count == 1) {
return 'one';
} else if ($count == 2) {
return 'two';
} else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 10) {
return 'few';
} else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 99) {
return 'many';
} else {
return 'other';
}
case 'pt':
case 'am':
case 'bh':
case 'fil':
case 'tl':
case 'guw':
case 'hi':
case 'ln':
case 'mg':
case 'nso':
case 'ti':
case 'wa':
if ($count == 0 || $count == 1) {
return 'one';
} else {
return 'other';
}
case 'fr':
if ($count >= 0 and $count < 2) {
return 'one';
} else {
return 'other';
}
case 'lv':
if ($count == 0) {
return 'zero';
} else if ($count % 10 == 1 && $count % 100 != 11) {
return 'one';
} else {
return 'other';
}
case 'ga':
case 'se':
case 'sma':
case 'smi':
case 'smj':
case 'smn':
case 'sms':
if ($count == 1) {
return 'one';
} else if ($count == 2) {
return 'two';
} else {
return 'other';
}
case 'ro':
case 'mo':
if ($count == 1) {
return 'one';
} else if (is_int($count) && $count == 0 && ($i = $count % 100) >= 1 && $i <= 19) {
return 'few';
} else {
return 'other';
}
case 'lt':
if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) {
return 'one';
} else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 9 && ($i = $count % 100) < 11 && $i > 19) {
return 'few';
} else {
return 'other';
}
case 'hr':
case 'ru':
case 'sr':
case 'uk':
case 'be':
case 'bs':
case 'sh':
if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) {
return 'one';
} else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 && ($i = $count % 100) < 12 && $i > 14) {
return 'few';
} else if (is_int($count) && ($count % 10 == 0 || (($i = $count % 10) >= 5 && $i <= 9) || (($i = $count % 100) >= 11 && $i <= 14))) {
return 'many';
} else {
return 'other';
}
case 'cs':
case 'sk':
if ($count == 1) {
return 'one';
} else if (is_int($count) && $count >= 2 && $count <= 4) {
return 'few';
} else {
return 'other';
}
case 'pl':
if ($count == 1) {
return 'one';
} else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 &&
($i = $count % 100) < 12 && $i > 14 && ($i = $count % 100) < 22 && $i > 24) {
return 'few';
} else {
return 'other';
}
case 'sl':
if ($count % 100 == 1) {
return 'one';
} else if ($count % 100 == 2) {
return 'two';
} else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 4) {
return 'few';
} else {
return 'other';
}
case 'mt':
if ($count == 1) {
return 'one';
} else if ($count == 0 || is_int($count) && ($i = $count % 100) >= 2 && $i <= 10) {
return 'few';
} else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 19) {
return 'many';
} else {
return 'other';
}
case 'mk':
if ($count % 10 == 1) {
return 'one';
} else {
return 'other';
}
case 'cy':
if ($count == 1) {
return 'one';
} else if ($count == 2) {
return 'two';
} else if ($count == 8 || $count == 11) {
return 'many';
} else {
return 'other';
}
default: // en, de, etc.
return $count == 1 ? 'one' : 'other';
}
}
// @todo Might want to add a localizable language name as well.
public function initialize_language_data() {
$l["af_ZA"] = "Afrikaans"; // Afrikaans
$l["ar_SA"] = "العربية"; // Arabic
$l["be_BY"] = "Беларускі"; // Belarusian
$l["bg_BG"] = "български"; // Bulgarian
$l["ca_ES"] = "Catalan"; // Catalan
$l["cs_CZ"] = "čeština"; // Czech
$l["da_DK"] = "Dansk"; // Danish
$l["de_DE"] = "Deutsch"; // German
$l["el_GR"] = "Greek"; // Greek
$l["en_GB"] = "English (UK)"; // English (UK)
$l["en_US"] = "English (US)"; // English (US)
$l["es_AR"] = "Español (AR)"; // Spanish (AR)
$l["es_ES"] = "Español"; // Spanish (ES)
$l["es_MX"] = "Español (MX)"; // Spanish (MX)
$l["et_EE"] = "Eesti"; // Estonian
$l["eu_ES"] = "Euskara"; // Basque
$l["fa_IR"] = "فارس"; // Farsi
$l["fi_FI"] = "Suomi"; // Finnish
$l["fo_FO"] = "Føroyskt"; // Faroese
$l["fr_FR"] = "Français"; // French
$l["ga_IE"] = "Gaeilge"; // Irish
$l["he_IL"] = "עברית"; // Hebrew
$l["hu_HU"] = "Magyar"; // Hungarian
$l["is_IS"] = "Icelandic"; // Icelandic
$l["it_IT"] = "Italiano"; // Italian
$l["ja_JP"] = "日本語"; // Japanese
$l["ko_KR"] = "한국어"; // Korean
$l["lt_LT"] = "Lietuvių"; // Lithuanian
$l["lv_LV"] = "Latviešu"; // Latvian
$l["nl_NL"] = "Nederlands"; // Dutch
$l["no_NO"] = "Norsk bokmål"; // Norwegian
$l["pl_PL"] = "Polski"; // Polish
$l["pt_BR"] = "Português do Brasil"; // Portuguese (BR)
$l["pt_PT"] = "Português ibérico"; // Portuguese (PT)
$l["ro_RO"] = "Română"; // Romanian
$l["ru_RU"] = "Русский"; // Russian
$l["sk_SK"] = "Slovenčina"; // Slovak
$l["sl_SI"] = "Slovenščina"; // Slovenian
$l["sr_CS"] = "Srpski"; // Serbian
$l["sv_SE"] = "Svenska"; // Swedish
$l["tr_TR"] = "Türkçe"; // Turkish
$l["uk_UA"] = "українська"; // Ukrainian
$l["vi_VN"] = "Tiếng Việt"; // Vietnamese
$l["zh_CN"] = "简体中文"; // Chinese (CN)
$l["zh_TW"] = "繁體中文"; // Chinese (TW)
asort($l, SORT_LOCALE_STRING);
// Language subtag to (default) locale mapping
foreach ($l as $locale => $name) {
list ($language) = explode("_", $locale . "_");
// The first one mentioned is the default
if (!isset($d[$language])) {
$d[$language] = $locale;
}
}
$this->locales = $l;
$this->language_subtag_to_locale = $d;
}
public function set_request_locale() {
// 1. Check the session specific preference (cookie)
$locale = $this->cookie_locale();
// 2. Check the user's preference
if (!$locale) {
$locale = identity::active_user()->locale;
}
// 3. Check the browser's / OS' preference
if (!$locale) {
$locale = $this->_locale_from_http_request();
}
// If we have any preference, override the site's default locale
if ($locale) {
$this->locale($locale);
}
}
/**
* Returns the best match comparing the HTTP accept-language header
* with the installed locales.
* @todo replace this with request::accepts_language() when we upgrade to Kohana 2.4
*/
private function _locale_from_http_request() {
$http_accept_language = Input::instance()->server("HTTP_ACCEPT_LANGUAGE");
if ($http_accept_language) {
// Parse the HTTP header and build a preference list
// Example value: "de,en-us;q=0.7,en-uk,fr-fr;q=0.2"
$locale_preferences = array();
foreach (explode(",", $http_accept_language) as $code) {
list ($requested_locale, $qvalue) = explode(";", $code . ";");
$requested_locale = trim($requested_locale);
$qvalue = trim($qvalue);
if (preg_match("/^([a-z]{2,3})(?:[_-]([a-zA-Z]{2}))?/", $requested_locale, $matches)) {
$requested_locale = strtolower($matches[1]);
if (!empty($matches[2])) {
$requested_locale .= "_" . strtoupper($matches[2]);
}
$requested_locale = trim(str_replace("-", "_", $requested_locale));
if (!strlen($qvalue)) {
// If not specified, default to 1.
$qvalue = 1;
} else {
// qvalue is expected to be something like "q=0.7"
list ($ignored, $qvalue) = explode("=", $qvalue . "==");
$qvalue = floatval($qvalue);
}
// Group by language to boost inexact same-language matches
list ($language) = explode("_", $requested_locale . "_");
if (!isset($locale_preferences[$language])) {
$locale_preferences[$language] = array();
}
$locale_preferences[$language][$requested_locale] = $qvalue;
}
}
// Compare and score requested locales with installed ones
$scored_locales = array();
foreach ($locale_preferences as $language => $requested_locales) {
// Inexact match adjustment (same language, different region)
$fallback_adjustment_factor = 0.95;
if (count($requested_locales) > 1) {
// Sort by qvalue, descending
$qvalues = array_values($requested_locales);
rsort($qvalues);
// Ensure inexact match scores worse than 2nd preference in same language.
$fallback_adjustment_factor *= $qvalues[1];
}
foreach ($requested_locales as $requested_locale => $qvalue) {
list ($matched_locale, $match_score) =
$this->_locale_match_score($requested_locale, $qvalue, $fallback_adjustment_factor);
if ($matched_locale &&
(!isset($scored_locales[$matched_locale]) ||
$match_score > $scored_locales[$matched_locale])) {
$scored_locales[$matched_locale] = $match_score;
}
}
}
arsort($scored_locales);
list ($locale) = each($scored_locales);
return $locale;
}
return null;
}
private function _locale_match_score($requested_locale, $qvalue, $adjustment_factor) {
$installed = $this->installed();
if (isset($installed[$requested_locale])) {
return array($requested_locale, $qvalue);
}
list ($language) = explode("_", $requested_locale . "_");
if (isset($this->language_subtag_to_locale[$language]) &&
isset($installed[$this->language_subtag_to_locale[$language]])) {
$score = $adjustment_factor * $qvalue;
return array($this->language_subtag_to_locale[$language], $score);
}
return array(null, 0);
}
public function cookie_locale() {
// Can't use Input framework for client side cookies since
// they're not signed.
$cookie_data = isset($_COOKIE["g_locale"]) ? $_COOKIE["g_locale"] : null;
$locale = null;
if ($cookie_data) {
if (preg_match("/^([a-z]{2,3}(?:_[A-Z]{2})?)$/", trim($cookie_data), $matches)) {
$requested_locale = $matches[1];
$installed_locales = locales::installed();
if (isset($installed_locales[$requested_locale])) {
$locale = $requested_locale;
}
}
}
return $locale;
}
public function update_installed($locales) {
// Ensure that the default is included...
$default = module::get_var("gallery", "default_locale");
$locales = in_array($default, $locales)
? $locales
: array_merge($locales, array($default));
module::set_var("gallery", "installed_locales", join("|", $locales));
// Clear the cache
$this->locales = null;
}
public function display_name($locale=null) {
if (empty($this->locales)) {
$this->initialize_language_data();
}
$locale or $locale = Gallery_I18n::instance()->locale();
return $this->locales[$locale];
}
}

View File

@ -1,4 +0,0 @@
name = Multi-Language Support
description = "Provide support for internationalization (i18n) and localization(i10n)"
version = 1

View File

@ -1,115 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<script type="text/javascript">
var old_default_locale = <?= html::js_string($default_locale) ?>;
$("#g-languages-form").ready(function() {
$("input[name='installed_locales[]']").change(function (event) {
if (this.checked) {
$("input[type='radio'][value='" + this.value + "']").enable();
} else {
if ($("input[type='radio'][value='" + this.value + "']").selected()) { // if you deselect your default language, switch to some other installed language
$("input[type='radio'][value='" + old_default_locale + "']").attr("checked", "checked");
}
$("input[type='radio'][value='" + this.value + "']").attr("disabled", "disabled");
}
});
$("#g-languages-form").ajaxForm({
dataType: "json",
success: function(data) {
if (data.result == "success") {
el = $('<a href="' + <?= html::js_string(url::site("admin/maintenance/start/gallery_task::update_l10n?csrf=$csrf")) ?> + '"></a>'); // this is a little hack to trigger the update_l10n task in a dialog
el.gallery_dialog();
el.trigger('click');
}
}
});
});
</script>
<div class="g-block">
<h1> <?= t("Languages and translation") ?> </h1>
<div class="g-block-content">
<div id="g-languages" class="g-block">
<h2> <?= t("Languages") ?> </h2>
<p>
<?= t("Install new languages, update installed ones and set the default language for your Gallery.") ?>
</p>
<div class="g-block-content ui-helper-clearfix">
<form id="g-languages-form" method="post" action="<?= url::site("admin/languages/save") ?>">
<?= access::csrf_form_field() ?>
<table class="g-left">
<tr>
<th> <?= t("Installed") ?> </th>
<th> <?= t("Language") ?> </th>
<th> <?= t("Default language") ?> </th>
</tr>
<? $i = 0 ?>
<? foreach ($available_locales as $code => $display_name): ?>
<? if ($i == (int) (count($available_locales)/2)): ?>
</table>
<table>
<tr>
<th> <?= t("Installed") ?> </th>
<th> <?= t("Language") ?> </th>
<th> <?= t("Default language") ?> </th>
</tr>
<? endif ?>
<tr class="<?= (isset($installed_locales[$code])) ? "g-available" : "" ?><?= ($default_locale == $code) ? " g-selected" : "" ?>">
<td> <?= form::checkbox("installed_locales[]", $code, isset($installed_locales[$code])) ?> </td>
<td> <?= $display_name ?> </td>
<td>
<?= form::radio("default_locale", $code, ($default_locale == $code), ((isset($installed_locales[$code]))?'':'disabled="disabled"') ) ?>
</td>
</tr>
<? $i++ ?>
<? endforeach ?>
</table>
<input type="submit" value="<?= t("Update languages")->for_html_attr() ?>" />
</form>
</div>
</div>
<div id="g-translations" class="g-block">
<h2> <?= t("Translations") ?> </h2>
<p>
<?= t("Create your own translations and share them with the rest of the Gallery community.") ?>
</p>
<div class="g-block-content">
<a href="http://codex.gallery2.org/Gallery3:Localization" target="_blank"
class="g-right ui-state-default ui-corner-all ui-icon ui-icon-help"
title="<?= t("Localization documentation")->for_html_attr() ?>">
<?= t("Localization documentation") ?>
</a>
<h3><?= t("Translating Gallery") ?></h3>
<p><?= t("Follow these steps to begin translating Gallery.") ?></p>
<ol>
<li><?= t("Make sure the target language is installed and up to date (check above).") ?></li>
<li><?= t("Make sure you have selected the right target language (currently %default_locale).",
array("default_locale" => locales::display_name())) ?></li>
<li><?= t("Start the translation mode and the translation interface will appear at the bottom of each Gallery page.") ?></li>
</ol>
<a href="<?= url::site("l10n_client/toggle_l10n_mode?csrf=".access::csrf_token()) ?>"
class="g-button ui-state-default ui-corner-all ui-icon-left">
<span class="ui-icon ui-icon-power"></span>
<? if (Session::instance()->get("l10n_mode", false)): ?>
<?= t("Stop translation mode") ?>
<? else: ?>
<?= t("Start translation mode") ?>
<? endif ?>
</a>
<h3><?= t("Sharing your translations") ?></h3>
<?= $share_translations_form ?>
</div>
</div>
</div>
</div>

View File

@ -1,82 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<div id="l10n-client" class="hidden">
<div class="labels">
<span id="l10n-client-toggler">
<a id="g-minimize-l10n">_</a>
<a id="g-close-l10n" title="<?= t("Stop the translation mode")->for_html_attr() ?>"
href="<?= html::clean_attribute(url::site("l10n_client/toggle_l10n_mode?csrf=".access::csrf_token())) ?>">X</a>
</span>
<div class="label strings"><h2><?= t("Page text") ?>
<? if (!Input::instance()->get('show_all_l10n_messages')): ?>
<a style="background-color:#fff" href="<?= url::site("admin/languages?show_all_l10n_messages=1") ?>"><?= t("(Show all)") ?></a>
<? endif; ?>
</h2></div>
<div class="label source"><h2><?= t("Source") ?></div>
<div class="label translation"><h2><?= t("Translation to %language",
array("language" => locales::display_name())) ?></h2></div>
</div>
<div id="l10n-client-string-select">
<ul class="string-list">
<? foreach ($string_list as $string): ?>
<li class="<?= $string["translation"] === "" ? "untranslated" : "translated" ?>">
<? if (is_array($string["source"])): ?>
[one] - <?= $string["source"]["one"] ?><br/>
[other] - <?= $string["source"]["other"] ?>
<? else: ?>
<?= $string["source"] ?>
<? endif; ?>
</li>
<? endforeach; ?>
</ul>
<?= $l10n_search_form ?>
</div>
<div id="l10n-client-string-editor">
<div class="source">
<p class="source-text"></p>
<p id="source-text-tmp-space" style="display:none"></p>
</div>
<div class="translation">
<form method="post" action="<?= url::site("l10n_client/save") ?>" id="g-l10n-client-save-form">
<?= access::csrf_form_field() ?>
<?= form::hidden("l10n-message-key") ?>
<?= form::textarea("l10n-edit-translation", "", ' id="l10n-edit-translation" rows="5" class="translationField"') ?>
<div id="plural-zero" class="translationField hidden">
<label for="l10n-edit-plural-translation-zero">[zero]</label>
<?= form::textarea("l10n-edit-plural-translation-zero", "", ' rows="2"') ?>
</div>
<div id="plural-one" class="translationField hidden">
<label for="l10n-edit-plural-translation-one">[one]</label>
<?= form::textarea("l10n-edit-plural-translation-one", "", ' rows="2"') ?>
</div>
<div id="plural-two" class="translationField hidden">
<label for="l10n-edit-plural-translation-two">[two]</label>
<?= form::textarea("l10n-edit-plural-translation-two", "", ' rows="2"') ?>
</div>
<div id="plural-few" class="translationField hidden">
<label for="l10n-edit-plural-translation-few">[few]</label>
<?= form::textarea("l10n-edit-plural-translation-few", "", ' rows="2"') ?>
</div>
<div id="plural-many" class="translationField hidden">
<label for="l10n-edit-plural-translation-many">[many]</label>
<?= form::textarea("l10n-edit-plural-translation-many", "", ' rows="2"') ?>
</div>
<div id="plural-other" class="translationField hidden">
<label for="l10n-edit-plural-translation-other">[other]</label>
(<a href="http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html"><?= t("learn more about plural forms") ?></a>)
<?= form::textarea("l10n-edit-plural-translation-other", "", ' rows="2"') ?>
</div>
<input type="submit" name="l10n-edit-save" value="<?= t("Save translation")->for_html_attr() ?>"/>
<a href="javascript: Gallery.l10nClient.copySourceText()"
class="g-button ui-state-default ui-corner-all"><?= t("Copy source text") ?></a>
</form>
</div>
</div>
<script type="text/javascript">
var MSG_TRANSLATE_TEXT = <?= t("Translate text")->for_js() ?>;
var l10n_client_data = <?= json_encode($string_list) ?>;
var plural_forms = <?= json_encode($plural_forms) ?>;
var toggle_l10n_mode_url = <?= html::js_string(url::site("l10n_client/toggle_l10n_mode")) ?>;
var csrf = <?= html::js_string(access::csrf_token()) ?>;
</script>
</div>

View File

@ -1,19 +0,0 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<?= form::dropdown("g-select-session-locale", $installed_locales, $selected) ?>
<script type="text/javascript">
$("select[name=g-select-session-locale]").change(function() {
var old_locale_preference = <?= html::js_string($selected) ?>;
var locale = $(this).val();
if (old_locale_preference == locale) {
return;
}
var expires = -1;
if (locale) {
expires = 365;
}
$.cookie("g_locale", locale, {"expires": expires, "path": "/"});
window.location.reload(true);
});
</script>