diff options
24 files changed, 515 insertions, 32 deletions
diff --git a/chrome/browser/extensions/extension_l10n_util.cc b/chrome/browser/extensions/extension_l10n_util.cc index d13a97c..2c66ff9 100644 --- a/chrome/browser/extensions/extension_l10n_util.cc +++ b/chrome/browser/extensions/extension_l10n_util.cc @@ -12,9 +12,9 @@ #include "base/file_util.h" #include "base/string_util.h" #include "base/values.h" -#include "chrome/browser/extensions/extension_message_bundle.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/extensions/extension_message_bundle.h" #include "chrome/common/json_value_serializer.h" namespace errors = extension_manifest_errors; diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index 76f9057b..d241360 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -116,6 +116,12 @@ void ExtensionProcessManager::RegisterExtensionProcess( RenderProcessHost* rph = RenderProcessHost::FromID(process_id); rph->Send(new ViewMsg_Extension_UpdatePageActions(extension_id, page_action_ids)); + + // Send l10n messages to the renderer - if there are any. + if (extension->message_bundle()) { + rph->Send(new ViewMsg_Extension_SetL10nMessages( + extension_id, *extension->message_bundle()->dictionary())); + } } void ExtensionProcessManager::UnregisterExtensionProcess(int process_id) { diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index f6e2d6f..e381018 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -462,6 +462,8 @@ 'common/extensions/extension_error_reporter.h', 'common/extensions/extension_error_utils.cc', 'common/extensions/extension_error_utils.h', + 'common/extensions/extension_message_bundle.cc', + 'common/extensions/extension_message_bundle.h', 'common/extensions/extension_unpacker.cc', 'common/extensions/extension_unpacker.h', 'common/extensions/update_manifest.cc', @@ -1194,8 +1196,6 @@ 'browser/extensions/extension_install_ui.h', 'browser/extensions/extension_l10n_util.cc', 'browser/extensions/extension_l10n_util.h', - 'browser/extensions/extension_message_bundle.cc', - 'browser/extensions/extension_message_bundle.h', 'browser/extensions/extension_message_service.cc', 'browser/extensions/extension_message_service.h', 'browser/extensions/extension_browser_event_router.cc', @@ -4193,7 +4193,6 @@ 'browser/encoding_menu_controller_unittest.cc', 'browser/extensions/extension_file_util_unittest.cc', 'browser/extensions/extension_l10n_util_unittest.cc', - 'browser/extensions/extension_message_bundle_unittest.cc', 'browser/extensions/extension_messages_unittest.cc', 'browser/extensions/extension_process_manager_unittest.cc', 'browser/extensions/extension_ui_unittest.cc', @@ -4315,6 +4314,7 @@ 'common/chrome_plugin_unittest.cc', 'common/common_param_traits_unittest.cc', 'common/extensions/extension_unittest.cc', + 'common/extensions/extension_message_bundle_unittest.cc', 'common/extensions/update_manifest_unittest.cc', 'common/extensions/url_pattern_unittest.cc', 'common/extensions/user_script_unittest.cc', diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index f9c5883..e26a2d2 100755 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -1114,10 +1114,37 @@ ] } ] + }, + { + "name": "getMessage", + "type": "function", + "description": "Get a message from the extension language catalog, for a current locale.", + "parameters": [ + { "type": "string", + "name": "message_name", + "description": "Message name from the extension catalog." + }, + { + "choices": [ + { "type": "string" }, + { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "maxItems": 9 + } + ], + "optional": true, + "description": "1 - 9 substitution parameters, if the message requires any." + } + ], + "returns": { + "type": "string", + "description": "Message localized for current locale." + } } ], - "events": [ - ] + "events": [] }, { "namespace": "devtools", diff --git a/chrome/common/extensions/docs/i18n.html b/chrome/common/extensions/docs/i18n.html index bb16d05..ba1c494 100755 --- a/chrome/common/extensions/docs/i18n.html +++ b/chrome/common/extensions/docs/i18n.html @@ -164,8 +164,10 @@ <li> <a href="#methods">Methods</a> <ol> - <li jsinstance="*0"> + <li jsinstance="0"> <a href="#method-getAcceptLanguages">getAcceptLanguages</a> + </li><li jsinstance="*1"> + <a href="#method-getMessage">getMessage</a> </li> </ol> </li> @@ -198,7 +200,8 @@ <!-- BEGIN AUTHORED CONTENT --> <p id="classSummary"> Use the <code>chrome.i18n</code> module to manipulate the i18n related browser -settings, such as the accept languages. +settings, such as the accept languages, or to get localized messages for current +locale. </p> <h3 id="overview-examples">Examples</h3> @@ -215,6 +218,18 @@ string by separating each accept-language with ','. }) } </pre> + +<p> +The following code gets localized message from the browser and displays it as a +string. It replaces two placeholders within the message with values arg1 and +arg2. +</p> + +<pre>function getMessage() { + var message = chrome.i18n.getMessage("click_here", ["arg1", "arg2"]); + document.getElementById("languageSpan").innerHTML = message; +} +</pre> <!-- END AUTHORED CONTENT --> </div> @@ -247,7 +262,7 @@ string by separating each accept-language with ','. <h3>Methods</h3> <!-- iterates over all functions --> - <div class="apiItem" jsinstance="*0"> + <div class="apiItem" jsinstance="0"> <a name="method-getAcceptLanguages"></a> <!-- method-anchor --> <h4>getAcceptLanguages</h4> @@ -388,6 +403,177 @@ string by separating each accept-language with ','. </div> <!-- /description --> + </div><div class="apiItem" jsinstance="*1"> + <a name="method-getMessage"></a> <!-- method-anchor --> + <h4>getMessage</h4> + + <div class="summary"><span>string</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.i18n.getMessage</span>(<span jsinstance="0" class="null"><span style="display: none; ">, </span><span>string</span> + <var><span>message_name</span></var></span><span jsinstance="*1" class="optional"><span>, </span><span>string or array of string</span> + <var><span><!--framePath //<!--frame0-->--></span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Get a message from the extension language catalog, for a current locale.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div jsinstance="0"> + <div> + <dt> + <var>message_name</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Message name from the extension catalog.</dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + </div> + </div><div jsinstance="*1"> + <div> + <dt> + <var style="display: none; ">paramName</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string or array of string</span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>1 - 9 substitution parameters, if the message requires any.</dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4>Returns</h4> + <dl> + <div> + <div> + <dt> + <var style="display: none; ">paramName</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Message localized for current locale.</dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div style="display: none; "> + <div> + <h4>Callback function</h4> + <p> + If you specify the <em>callback</em> parameter, + it should specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>);</pre> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + </div> + + </div> <!-- /description --> + </div> <!-- /apiItem --> </div> <!-- /apiGroup --> diff --git a/chrome/common/extensions/docs/static/i18n.html b/chrome/common/extensions/docs/static/i18n.html index 63fb883..2ce586f 100644 --- a/chrome/common/extensions/docs/static/i18n.html +++ b/chrome/common/extensions/docs/static/i18n.html @@ -3,7 +3,8 @@ <!-- BEGIN AUTHORED CONTENT --> <p id="classSummary"> Use the <code>chrome.i18n</code> module to manipulate the i18n related browser -settings, such as the accept languages. +settings, such as the accept languages, or to get localized messages for current +locale. </p> <h3 id="overview-examples">Examples</h3> @@ -21,4 +22,17 @@ function getAcceptLanguages() { }) } </pre> + +<p> +The following code gets localized message from the browser and displays it as a +string. It replaces two placeholders within the message with values arg1 and +arg2. +</p> + +<pre> +function getMessage() { + var message = chrome.i18n.getMessage("click_here", ["arg1", "arg2"]); + document.getElementById("languageSpan").innerHTML = message; +} +</pre> <!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 5589500..6c085ab 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -14,8 +14,8 @@ #include "base/scoped_ptr.h" #include "base/values.h" #include "base/version.h" -#include "chrome/browser/extensions/extension_message_bundle.h" #include "chrome/browser/extensions/user_script_master.h" +#include "chrome/common/extensions/extension_message_bundle.h" #include "chrome/common/extensions/user_script.h" #include "chrome/common/extensions/url_pattern.h" #include "chrome/common/page_action.h" diff --git a/chrome/browser/extensions/extension_message_bundle.cc b/chrome/common/extensions/extension_message_bundle.cc index 34fd1a2..980dd08 100644 --- a/chrome/browser/extensions/extension_message_bundle.cc +++ b/chrome/common/extensions/extension_message_bundle.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/extensions/extension_message_bundle.h" +#include "chrome/common/extensions/extension_message_bundle.h" #include <string> @@ -246,9 +246,15 @@ bool ExtensionMessageBundle::IsValidName(const str& name) { std::string ExtensionMessageBundle::GetL10nMessage( const std::string& name) const { + return GetL10nMessage(name, dictionary_); +} + +// static +std::string ExtensionMessageBundle::GetL10nMessage( + const std::string& name, const SubstitutionMap& dictionary) { SubstitutionMap::const_iterator it = - dictionary_.find(StringToLowerASCII(name)); - if (it != dictionary_.end()) { + dictionary.find(StringToLowerASCII(name)); + if (it != dictionary.end()) { return it->second; } diff --git a/chrome/browser/extensions/extension_message_bundle.h b/chrome/common/extensions/extension_message_bundle.h index 548f7ec..4c57117 100644 --- a/chrome/browser/extensions/extension_message_bundle.h +++ b/chrome/common/extensions/extension_message_bundle.h @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_MESSAGE_BUNDLE_H_ -#define CHROME_BROWSER_EXTENSIONS_EXTENSION_MESSAGE_BUNDLE_H_ +#ifndef CHROME_COMMON_EXTENSIONS_EXTENSION_MESSAGE_BUNDLE_H_ +#define CHROME_COMMON_EXTENSIONS_EXTENSION_MESSAGE_BUNDLE_H_ +#include <map> #include <string> -#include "base/hash_tables.h" #include "base/values.h" // Contains localized extension messages for one locale. Any messages that the // locale does not provide are pulled from the default locale. class ExtensionMessageBundle { public: - typedef base::hash_map<std::string, std::string> SubstitutionMap; + typedef std::map<std::string, std::string> SubstitutionMap; // JSON keys of interest for messages file. static const wchar_t* kContentKey; @@ -45,6 +45,10 @@ class ExtensionMessageBundle { // #define GetMessage GetMessageW override in Chrome code. std::string GetL10nMessage(const std::string& name) const; + // Get message from the given catalog with given key. + static std::string GetL10nMessage(const std::string& name, + const SubstitutionMap& dictionary); + // Number of messages in the catalog. // Used for unittesting only. size_t size() const { return dictionary_.size(); } @@ -71,6 +75,9 @@ class ExtensionMessageBundle { template<typename str> static bool IsValidName(const str& name); + // Getter for dictionary_. + const SubstitutionMap* dictionary() const { return &dictionary_; } + private: // Use Create to create ExtensionMessageBundle instance. ExtensionMessageBundle(); @@ -107,4 +114,4 @@ class ExtensionMessageBundle { SubstitutionMap dictionary_; }; -#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MESSAGE_BUNDLE_H_ +#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_MESSAGE_BUNDLE_H_ diff --git a/chrome/browser/extensions/extension_message_bundle_unittest.cc b/chrome/common/extensions/extension_message_bundle_unittest.cc index d7344b3..df2b7c7 100644 --- a/chrome/browser/extensions/extension_message_bundle_unittest.cc +++ b/chrome/common/extensions/extension_message_bundle_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/extensions/extension_message_bundle.h" +#include "chrome/common/extensions/extension_message_bundle.h" #include <string> diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 4eec034..22ba42a 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -6,6 +6,7 @@ // header guard. // See ipc_message_macros.h for explanation of the macros and passes. +#include <map> #include <string> #include <vector> @@ -39,6 +40,11 @@ // TODO(mpcomplete): rename ViewMsg and ViewHostMsg to something that makes // more sense with our current design. +// IPC_MESSAGE macros choke on extra , in the std::map, when expanding. We need +// to typedef it to avoid that. +// Substitution map for l10n messages. +typedef std::map<std::string, std::string> SubstitutionMap; + //----------------------------------------------------------------------------- // RenderView messages // These are messages sent from the browser to the renderer process. @@ -653,6 +659,12 @@ IPC_BEGIN_MESSAGES(View) std::string /* extension_id */, std::vector<std::string> /* page_action_ids */) + // Tell the renderer process all known localized messages for a particular + // extension. + IPC_MESSAGE_CONTROL2(ViewMsg_Extension_SetL10nMessages, + std::string /* extension_id */, + SubstitutionMap /* l10n messages */) + // Changes the text direction of the currently selected input field (if any). IPC_MESSAGE_ROUTED1(ViewMsg_SetTextDirection, WebKit::WebTextDirection /* direction */) diff --git a/chrome/renderer/extensions/extension_api_client_unittest.cc b/chrome/renderer/extensions/extension_api_client_unittest.cc index 14ab617..1a06889 100644 --- a/chrome/renderer/extensions/extension_api_client_unittest.cc +++ b/chrome/renderer/extensions/extension_api_client_unittest.cc @@ -605,3 +605,21 @@ TEST_F(ExtensionAPIClientTest, GetAcceptLanguages) { ExpectJsPass("chrome.i18n.getAcceptLanguages(function(){})", "i18n.getAcceptLanguages", "null"); } + +TEST_F(ExtensionAPIClientTest, GetL10nMessage) { + ExpectJsFail("chrome.i18n.getMessage()", + "Uncaught Error: Parameter 0 is required."); + + ExpectJsFail("chrome.i18n.getMessage(1)", + "Uncaught Error: Invalid value for argument 0. " + "Expected 'string' but got 'integer'."); + + ExpectJsFail("chrome.i18n.getMessage('name', [])", + "Uncaught Error: Invalid value for argument 1. Value does not " + "match any valid type choices."); + + ExpectJsFail("chrome.i18n.getMessage('name', ['p1', 'p2', 'p3', 'p4', 'p5', " + "'p6', 'p7', 'p8', 'p9', 'p10'])", + "Uncaught Error: Invalid value for argument 1. Value does not " + "match any valid type choices."); +} diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index 0fc9438..1a461a8 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -2,10 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <map> +#include <set> +#include <string> +#include <vector> + #include "chrome/renderer/extensions/extension_process_bindings.h" #include "base/singleton.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_message_bundle.h" #include "chrome/common/extensions/url_pattern.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" @@ -41,6 +47,13 @@ typedef std::map<std::string, bool> PermissionsMap; // A map of extension ID to permissions map. typedef std::map<std::string, PermissionsMap> ExtensionPermissionsMap; +// A map of message name to message. +typedef std::map<std::string, std::string> L10nMessagesMap; + +// A map of extension ID to l10n message map. +typedef std::map<std::string, L10nMessagesMap > + ExtensionToL10nMessagesMap; + const char kExtensionName[] = "chrome/ExtensionProcessBindings"; const char* kExtensionDeps[] = { BaseJsV8Extension::kName, @@ -54,6 +67,7 @@ struct SingletonData { std::set<std::string> function_names_; PageActionIdMap page_action_ids_; ExtensionPermissionsMap permissions_; + ExtensionToL10nMessagesMap extension_l10n_messages_map_; }; static std::set<std::string>* GetFunctionNameSet() { @@ -68,6 +82,20 @@ static PermissionsMap* GetPermissionsMap(const std::string& extension_id) { return &Singleton<SingletonData>()->permissions_[extension_id]; } +static ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() { + return &Singleton<SingletonData>()->extension_l10n_messages_map_; +} + +static L10nMessagesMap* GetL10nMessagesMap(const std::string extension_id) { + ExtensionToL10nMessagesMap::iterator it = + Singleton<SingletonData>()->extension_l10n_messages_map_.find(extension_id); + if (it != Singleton<SingletonData>()->extension_l10n_messages_map_.end()) { + return &(it->second); + } else { + return NULL; + } +} + class ExtensionImpl : public ExtensionBase { public: ExtensionImpl() : ExtensionBase( @@ -109,6 +137,8 @@ class ExtensionImpl : public ExtensionBase { return v8::FunctionTemplate::New(StartRequest); } else if (name->Equals(v8::String::New("GetRenderViewId"))) { return v8::FunctionTemplate::New(GetRenderViewId); + } else if (name->Equals(v8::String::New("GetL10nMessage"))) { + return v8::FunctionTemplate::New(GetL10nMessage); } return ExtensionBase::GetNativeFunction(name); @@ -131,7 +161,7 @@ class ExtensionImpl : public ExtensionBase { // TODO(erikkay) for now, special case mole as a type of toolstrip. // Perhaps this isn't the right long-term thing to do. - if (match == ViewType::EXTENSION_TOOLSTRIP && + if (match == ViewType::EXTENSION_TOOLSTRIP && type == ViewType::EXTENSION_MOLE) { return true; } @@ -255,6 +285,50 @@ class ExtensionImpl : public ExtensionBase { return page_action_vector; } + static v8::Handle<v8::Value> GetL10nMessage(const v8::Arguments& args) { + if (args.Length() != 2 || !args[0]->IsString()) { + NOTREACHED() << "Bad arguments"; + return v8::Undefined(); + } + + L10nMessagesMap* l10n_messages = + GetL10nMessagesMap(ExtensionIdForCurrentContext()); + if (!l10n_messages) + return v8::Undefined(); + + std::string message_name = *v8::String::AsciiValue(args[0]); + std::string message = + ExtensionMessageBundle::GetL10nMessage(message_name, *l10n_messages); + + std::vector<string16> substitutions; + if (args[1]->IsNull() || args[1]->IsUndefined()) { + // chrome.i18n.getMessage("message_name"); + // chrome.i18n.getMessage("message_name", null); + return v8::String::New(message.c_str()); + } else if (args[1]->IsString()) { + // chrome.i18n.getMessage("message_name", "one param"); + std::string substitute = *v8::String::Utf8Value(args[1]->ToString()); + substitutions.push_back(UTF8ToUTF16(substitute)); + } else if (args[1]->IsArray()) { + // chrome.i18n.getMessage("message_name", ["more", "params"]); + v8::Array* placeholders = static_cast<v8::Array*>(*args[1]); + uint32_t count = placeholders->Length(); + DCHECK(count > 0 && count <= 9); + for (uint32_t i = 0; i < count; ++i) { + std::string substitute = + *v8::String::Utf8Value( + placeholders->Get(v8::Integer::New(i))->ToString()); + substitutions.push_back(UTF8ToUTF16(substitute)); + } + } else { + NOTREACHED() << "Couldn't parse second parameter."; + return v8::Undefined(); + } + + return v8::String::New(UTF16ToUTF8(ReplaceStringPlaceholders( + UTF8ToUTF16(message), substitutions, NULL)).c_str()); + } + // Starts an API request to the browser, with an optional callback. The // callback will be dispatched to EventBindings::HandleResponse. static v8::Handle<v8::Value> StartRequest(const v8::Arguments& args) { @@ -331,7 +405,7 @@ void ExtensionProcessBindings::HandleResponse(int request_id, bool success, argv[4] = v8::String::New(error.c_str()); v8::Handle<v8::Value> retval = bindings_utils::CallFunctionInContext( request->second->context, "handleResponse", arraysize(argv), argv); - // In debug, the js will validate the callback parameters and return a + // In debug, the js will validate the callback parameters and return a // string if a validation error has occured. #ifdef _DEBUG if (!retval.IsEmpty() && !retval->IsUndefined()) { @@ -357,6 +431,15 @@ void ExtensionProcessBindings::SetPageActions( } // static +void ExtensionProcessBindings::SetL10nMessages( + const std::string& extension_id, + const std::map<std::string, std::string>& l10n_messages) { + ExtensionToL10nMessagesMap& l10n_messages_map = + *GetExtensionToL10nMessagesMap(); + l10n_messages_map[extension_id] = l10n_messages; +} + +// static void ExtensionProcessBindings::SetAPIPermissions( const std::string& extension_id, const std::vector<std::string>& permissions) { diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h index 1777658..77e5854 100644 --- a/chrome/renderer/extensions/extension_process_bindings.h +++ b/chrome/renderer/extensions/extension_process_bindings.h @@ -40,6 +40,11 @@ class ExtensionProcessBindings { static void SetHostPermissions(const GURL& extension_url, const std::vector<URLPattern>& permissions); + // Set l10n messages for a particular extension. + static void SetL10nMessages( + const std::string& extension_id, + const std::map<std::string, std::string>& l10n_messages); + // Check if the extension in the currently running context has permission to // access the given extension function. Must be called with a valid V8 // context in scope. diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 788b852..d753556 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -5,6 +5,7 @@ #include "chrome/renderer/render_thread.h" #include <algorithm> +#include <map> #include <vector> #include "base/command_line.h" @@ -238,6 +239,12 @@ void RenderThread::OnExtensionSetHostPermissions( ExtensionProcessBindings::SetHostPermissions(extension_url, permissions); } +void RenderThread::OnExtensionSetL10nMessages( + const std::string& extension_id, + const std::map<std::string, std::string>& l10n_messages) { + ExtensionProcessBindings::SetL10nMessages(extension_id, l10n_messages); +} + void RenderThread::OnControlMessageReceived(const IPC::Message& msg) { // App cache messages are handled by a delegate. if (appcache_dispatcher_->OnMessageReceived(msg)) @@ -273,6 +280,8 @@ void RenderThread::OnControlMessageReceived(const IPC::Message& msg) { OnExtensionSetAPIPermissions) IPC_MESSAGE_HANDLER(ViewMsg_Extension_SetHostPermissions, OnExtensionSetHostPermissions) + IPC_MESSAGE_HANDLER(ViewMsg_Extension_SetL10nMessages, + OnExtensionSetL10nMessages) IPC_END_MESSAGE_MAP() } diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index ab9a0b2..9fa7203 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -140,10 +140,15 @@ class RenderThread : public RenderThreadBase, void OnSetExtensionFunctionNames(const std::vector<std::string>& names); void OnPageActionsUpdated(const std::string& extension_id, const std::vector<std::string>& page_actions); - void OnExtensionSetAPIPermissions(const std::string& extension_id, - const std::vector<std::string>& permissions); - void OnExtensionSetHostPermissions(const GURL& extension_url, - const std::vector<URLPattern>& permissions); + void OnExtensionSetAPIPermissions( + const std::string& extension_id, + const std::vector<std::string>& permissions); + void OnExtensionSetHostPermissions( + const GURL& extension_url, + const std::vector<URLPattern>& permissions); + void OnExtensionSetL10nMessages( + const std::string& extension_id, + const std::map<std::string, std::string>& l10n_messages); void OnSetNextPageID(int32 next_page_id); void OnSetCSSColors(const std::vector<CSSColors::CSSColorMapping>& colors); void OnCreateNewView(gfx::NativeViewId parent_hwnd, diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index ff2fddd..710dad8 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -20,6 +20,7 @@ var chrome = chrome || {}; native function GetNextRequestId(); native function OpenChannelToTab(); native function GetRenderViewId(); + native function GetL10nMessage(); if (!chrome) chrome = {}; @@ -323,6 +324,11 @@ var chrome = chrome || {}; return tabIdProxy; } + apiFunctions["i18n.getMessage"].handleRequest = + function(message_name, placeholders) { + return GetL10nMessage(message_name, placeholders); + } + setupPageActionEvents(extensionId); setupBrowserActionEvent(extensionId); setupToolstripEvents(GetRenderViewId()); diff --git a/chrome/test/data/extensions/api_test/i18n/_locales/en_US/messages.json b/chrome/test/data/extensions/api_test/i18n/_locales/en_US/messages.json new file mode 100644 index 0000000..52b7cbf --- /dev/null +++ b/chrome/test/data/extensions/api_test/i18n/_locales/en_US/messages.json @@ -0,0 +1,30 @@ +{ + "chrome_extension_name": { + "message": "Api test extension" + }, + "simple_message": { + "message": "Simple message" + }, + "message_with_one_placeholder": { + "message": "Number of errors: $NR_OF_ERRORS$", + "placeholders": { + "nr_of_errors": { + "content": "$1" + } + } + }, + "message_with_placeholders": { + "message": "$Cira$ and $Somebody$ work for $GOOGLE$", + "placeholders": { + "cira": { + "content": "$1" + }, + "somebody": { + "content": "$2" + }, + "google": { + "content": "Google" + } + } + } +} diff --git a/chrome/test/data/extensions/api_test/i18n/manifest.json b/chrome/test/data/extensions/api_test/i18n/manifest.json index 3653d29..16e65ac 100644 --- a/chrome/test/data/extensions/api_test/i18n/manifest.json +++ b/chrome/test/data/extensions/api_test/i18n/manifest.json @@ -2,5 +2,6 @@ "name": "chrome.i18n", "version": "0.1", "description": "end-to-end browser test for chrome.i18n API", - "background_page": "test.html" + "background_page": "test.html", + "default_locale": "en_US" } diff --git a/chrome/test/data/extensions/api_test/i18n/test.js b/chrome/test/data/extensions/api_test/i18n/test.js index ab12d90..405de5b 100644 --- a/chrome/test/data/extensions/api_test/i18n/test.js +++ b/chrome/test/data/extensions/api_test/i18n/test.js @@ -1,5 +1,5 @@ // i18n api test -// browser_tests.exe --gtest_filter=ExtensionApiTest.I18N +// browser_tests.exe --gtest_filter=ExtensionApiTest.I18N --lib=browser_tests var testCallback = chrome.test.testCallback; @@ -10,5 +10,18 @@ chrome.test.runTests([ chrome.test.assertEq(results[0], "en-US"); chrome.test.assertEq(results[1], "en"); })); - } + }, + function getMessage() { + var message = chrome.i18n.getMessage("simple_message"); + chrome.test.assertEq(message, "Simple message"); + + message = chrome.i18n.getMessage("message_with_placeholders", + ["Cira", "John"]); + chrome.test.assertEq(message, "Cira and John work for Google"); + + message = chrome.i18n.getMessage("message_with_one_placeholder", "19"); + chrome.test.assertEq(message, "Number of errors: 19"); + + chrome.test.succeed(); + }, ]); diff --git a/chrome/test/data/extensions/samples/i18n/_locales/en_US/messages.json b/chrome/test/data/extensions/samples/i18n/_locales/en_US/messages.json new file mode 100644 index 0000000..9e86443 --- /dev/null +++ b/chrome/test/data/extensions/samples/i18n/_locales/en_US/messages.json @@ -0,0 +1,24 @@ +{ + "chrome_extension_name": { + "message": "AcceptLanguage" + }, + "chrome_extension_description": { + "message": "Returns accept languages of the browser" + }, + "click_here": { + "message": "Left click to list acceptLanguages." + }, + "chrome_accept_languages": { + "message": "$CHROME$ accepts $languages$ languages", + "placeholders": { + "chrome": { + "content": "Chrome", + "example": "Chrome" + }, + "languages": { + "content": "$1", + "example": "en-US,sr,de" + } + } + } +} diff --git a/chrome/test/data/extensions/samples/i18n/_locales/sr/messages.json b/chrome/test/data/extensions/samples/i18n/_locales/sr/messages.json new file mode 100644 index 0000000..30bd958 --- /dev/null +++ b/chrome/test/data/extensions/samples/i18n/_locales/sr/messages.json @@ -0,0 +1,24 @@ +{ + "chrome_extension_name": { + "message": "Прихватљиви језици" + }, + "chrome_extension_description": { + "message": "Језици које прегледач прихвата" + }, + "click_here": { + "message": "Кликните да излистате дозвољене језике." + }, + "chrome_accept_languages": { + "message": "$CHROME$ прихвата $languages$ језике.", + "placeholders": { + "chrome": { + "content": "Chrome", + "example": "Chrome" + }, + "languages": { + "content": "$1", + "example": "en-US,sr,de" + } + } + } +} diff --git a/chrome/test/data/extensions/samples/i18n/manifest.json b/chrome/test/data/extensions/samples/i18n/manifest.json index ec642b1..ca7fefd 100644 --- a/chrome/test/data/extensions/samples/i18n/manifest.json +++ b/chrome/test/data/extensions/samples/i18n/manifest.json @@ -2,5 +2,6 @@ "name": "AcceptLanguage", "description": "Returns accept languages of the browser", "version": "0.1", + "default_locale": "en_US", "toolstrips": ["toolstrip.html"] } diff --git a/chrome/test/data/extensions/samples/i18n/toolstrip.html b/chrome/test/data/extensions/samples/i18n/toolstrip.html index 677535a..970651a 100644 --- a/chrome/test/data/extensions/samples/i18n/toolstrip.html +++ b/chrome/test/data/extensions/samples/i18n/toolstrip.html @@ -7,17 +7,23 @@ LICENSE file. <html> <head> <script> + function init() { + document.getElementById("languageSpan").innerHTML = + chrome.i18n.getMessage("click_here"); + } + function getAcceptLanguages() { chrome.i18n.getAcceptLanguages(function(languageList) { var languages = languageList.join(","); - document.getElementById("languageSpan").innerHTML = languages; + document.getElementById("languageSpan").innerHTML = + chrome.i18n.getMessage("chrome_accept_languages", languages); }) } </script> </head> - <body> + <body onload="init()"> <div class="toolstrip-button" onclick="getAcceptLanguages();"> - <span id="languageSpan">GetAcceptLanguage</span> + <span id="languageSpan"></span> </div> </body> </html> |