summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/extension_l10n_util.cc2
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc6
-rw-r--r--chrome/chrome.gyp6
-rwxr-xr-xchrome/common/extensions/api/extension_api.json31
-rwxr-xr-xchrome/common/extensions/docs/i18n.html192
-rw-r--r--chrome/common/extensions/docs/static/i18n.html16
-rw-r--r--chrome/common/extensions/extension.h2
-rw-r--r--chrome/common/extensions/extension_message_bundle.cc (renamed from chrome/browser/extensions/extension_message_bundle.cc)12
-rw-r--r--chrome/common/extensions/extension_message_bundle.h (renamed from chrome/browser/extensions/extension_message_bundle.h)17
-rw-r--r--chrome/common/extensions/extension_message_bundle_unittest.cc (renamed from chrome/browser/extensions/extension_message_bundle_unittest.cc)2
-rw-r--r--chrome/common/render_messages_internal.h12
-rw-r--r--chrome/renderer/extensions/extension_api_client_unittest.cc18
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.cc87
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.h5
-rw-r--r--chrome/renderer/render_thread.cc9
-rw-r--r--chrome/renderer/render_thread.h13
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js6
-rw-r--r--chrome/test/data/extensions/api_test/i18n/_locales/en_US/messages.json30
-rw-r--r--chrome/test/data/extensions/api_test/i18n/manifest.json3
-rw-r--r--chrome/test/data/extensions/api_test/i18n/test.js17
-rw-r--r--chrome/test/data/extensions/samples/i18n/_locales/en_US/messages.json24
-rw-r--r--chrome/test/data/extensions/samples/i18n/_locales/sr/messages.json24
-rw-r--r--chrome/test/data/extensions/samples/i18n/manifest.json1
-rw-r--r--chrome/test/data/extensions/samples/i18n/toolstrip.html12
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>&lt;!--framePath //&lt;!--frame0--&gt;--&gt;</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>