Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: chrome/browser/extensions/extension_message_bundle.cc

Issue 202063: Implemented the rest of loading/parsing logic for extension i18n:... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/extension_message_bundle.h"
6
7 #include <string>
8
9 #include "base/hash_tables.h"
10 #include "base/scoped_ptr.h"
11 #include "base/string_util.h"
12 #include "base/values.h"
13
14 const wchar_t* ExtensionMessageBundle::kContentKey = L"content";
15 const wchar_t* ExtensionMessageBundle::kMessageKey = L"message";
16 const wchar_t* ExtensionMessageBundle::kPlaceholdersKey = L"placeholders";
17
18 const char* ExtensionMessageBundle::kPlaceholderBegin = "$";
19 const char* ExtensionMessageBundle::kPlaceholderEnd = "$";
20 const char* ExtensionMessageBundle::kMessageBegin = "__MSG_";
21 const char* ExtensionMessageBundle::kMessageEnd = "__";
22
23 const char* ExtensionMessageBundle::kExtensionName = "chrome_extension_name";
24 const char* ExtensionMessageBundle::kExtensionDescription =
25 "chrome_extension_description";
26
27 // Formats message in case we encounter a bad formed key in the JSON object.
28 // Returns false and sets |error| to actual error message.
29 static bool BadKeyMessage(const std::string& name, std::string* error) {
30 *error = StringPrintf("Name of a key \"%s\" is invalid. Only ASCII [a-z], "
31 "[A-Z], [0-9] and \"_\" are allowed.", name.c_str());
32 return false;
33 }
34
35 // static
36 ExtensionMessageBundle* ExtensionMessageBundle::Create(
37 const DictionaryValue& default_locale_catalog,
38 const DictionaryValue& current_locale_catalog,
39 std::string* error) {
40 scoped_ptr<ExtensionMessageBundle> message_bundle(
41 new ExtensionMessageBundle);
42 if (!message_bundle->Init(default_locale_catalog,
43 current_locale_catalog,
44 error))
45 return NULL;
46
47 return message_bundle.release();
48 }
49
50 bool ExtensionMessageBundle::Init(const DictionaryValue& default_locale_catalog,
51 const DictionaryValue& current_locale_catalog,
52 std::string* error) {
53 dictionary_.clear();
54
55 // Create a single dictionary out of default and current_locale catalogs.
56 // If message is missing from current_locale catalog, we take one from default
57 // catalog.
58 DictionaryValue::key_iterator key_it = current_locale_catalog.begin_keys();
59 for (; key_it != current_locale_catalog.end_keys(); ++key_it) {
60 std::string key(StringToLowerASCII(WideToUTF8(*key_it)));
61 if (!IsValidName(*key_it))
62 return BadKeyMessage(key, error);
63 std::string value;
64 if (!GetMessageValue(*key_it, current_locale_catalog, &value, error))
65 return false;
66 // Keys are not case-sensitive.
67 dictionary_[key] = value;
68 }
69
70 key_it = default_locale_catalog.begin_keys();
71 for (; key_it != default_locale_catalog.end_keys(); ++key_it) {
72 std::string key(StringToLowerASCII(WideToUTF8(*key_it)));
73 if (!IsValidName(*key_it))
74 return BadKeyMessage(key, error);
75 // Add only messages that are not provided by app_catalog.
76 if (dictionary_.find(key) != dictionary_.end())
77 continue;
78 std::string value;
79 if (!GetMessageValue(*key_it, default_locale_catalog, &value, error))
80 return false;
81 // Keys are not case-sensitive.
82 dictionary_[key] = value;
83 }
84
85 return true;
86 }
87
88 bool ExtensionMessageBundle::GetMessageValue(const std::wstring& wkey,
89 const DictionaryValue& catalog,
90 std::string* value,
91 std::string* error) const {
92 std::string key(WideToUTF8(wkey));
93 // Get the top level tree for given key (name part).
94 DictionaryValue* name_tree;
95 if (!catalog.GetDictionary(wkey, &name_tree)) {
96 *error = StringPrintf("Not a valid tree for key %s.", key.c_str());
97 return false;
98 }
99 // Extract message from it.
100 if (!name_tree->GetString(kMessageKey, value)) {
101 *error = StringPrintf("There is no \"%s\" element for key %s.",
102 WideToUTF8(kMessageKey).c_str(),
103 key.c_str());
104 return false;
105 }
106
107 SubstitutionMap placeholders;
108 if (!GetPlaceholders(*name_tree, key, &placeholders, error))
109 return false;
110
111 if (!ReplacePlaceholders(placeholders, value, error))
112 return false;
113
114 return true;
115 }
116
117 ExtensionMessageBundle::ExtensionMessageBundle() {
118 }
119
120 bool ExtensionMessageBundle::GetPlaceholders(const DictionaryValue& name_tree,
121 const std::string& name_key,
122 SubstitutionMap* placeholders,
123 std::string* error) const {
124 if (!name_tree.HasKey(kPlaceholdersKey))
125 return true;
126
127 DictionaryValue* placeholders_tree;
128 if (!name_tree.GetDictionary(kPlaceholdersKey, &placeholders_tree)) {
129 *error = StringPrintf("Not a valid \"%s\" element for key %s.",
130 WideToUTF8(kPlaceholdersKey).c_str(),
131 name_key.c_str());
132 return false;
133 }
134
135 for (DictionaryValue::key_iterator key_it = placeholders_tree->begin_keys();
136 key_it != placeholders_tree->end_keys();
137 ++key_it) {
138 DictionaryValue* placeholder;
139 std::string content_key = WideToUTF8(*key_it);
140 if (!IsValidName(*key_it))
141 return BadKeyMessage(content_key, error);
142 if (!placeholders_tree->GetDictionary(*key_it, &placeholder)) {
143 *error = StringPrintf("Invalid placeholder %s for key %s",
144 content_key.c_str(),
145 name_key.c_str());
146 return false;
147 }
148 std::string content;
149 if (!placeholder->GetString(kContentKey, &content)) {
150 *error = StringPrintf("Invalid \"%s\" element for key %s.",
151 WideToUTF8(kContentKey).c_str(),
152 name_key.c_str());
153 return false;
154 }
155 (*placeholders)[StringToLowerASCII(content_key)] = content;
156 }
157
158 return true;
159 }
160
161 bool ExtensionMessageBundle::ReplacePlaceholders(
162 const SubstitutionMap& placeholders,
163 std::string* message,
164 std::string* error) const {
165 return ReplaceVariables(placeholders,
166 kPlaceholderBegin,
167 kPlaceholderEnd,
168 message,
169 error);
170 }
171
172 bool ExtensionMessageBundle::ReplaceMessages(std::string* text,
173 std::string* error) const {
174 return ReplaceVariables(dictionary_, kMessageBegin, kMessageEnd, text, error);
175 }
176
177 // static
178 bool ExtensionMessageBundle::ReplaceVariables(
179 const SubstitutionMap& variables,
180 const std::string& var_begin_delimiter,
181 const std::string& var_end_delimiter,
182 std::string* message,
183 std::string* error) {
184 std::string::size_type beg_index = 0;
185 const std::string::size_type var_begin_delimiter_size =
186 var_begin_delimiter.size();
187 while (true) {
188 beg_index = message->find(var_begin_delimiter, beg_index);
189 if (beg_index == message->npos)
190 return true;
191
192 // Advance it immediately to the begining of possible variable name.
193 beg_index += var_begin_delimiter_size;
194 if (beg_index >= message->size())
195 return true;
196 std::string::size_type end_index =
197 message->find(var_end_delimiter, beg_index);
198 if (end_index == message->npos)
199 return true;
200
201 // Looking for 1 in substring of ...$1$....
202 const std::string& var_name =
203 message->substr(beg_index, end_index - beg_index);
204 if (!IsValidName(var_name))
205 continue;
206 SubstitutionMap::const_iterator it =
207 variables.find(StringToLowerASCII(var_name));
208 if (it == variables.end()) {
209 *error = StringPrintf("Variable %s%s%s used but not defined.",
210 var_begin_delimiter.c_str(),
211 var_name.c_str(),
212 var_end_delimiter.c_str());
213 return false;
214 }
215
216 // Replace variable with its value.
217 std::string value = it->second;
218 message->replace(beg_index - var_begin_delimiter_size,
219 end_index - beg_index + var_begin_delimiter_size +
220 var_end_delimiter.size(),
221 value);
222
223 // And position pointer to after the replacement.
224 beg_index += value.size() - var_begin_delimiter_size;
225 }
226
227 return true;
228 }
229
230 // static
231 template <typename str>
232 bool ExtensionMessageBundle::IsValidName(const str& name) {
233 if (name.empty())
234 return false;
235
236 for (str::const_iterator it = name.begin(); it != name.end(); ++it) {
237 // Allow only ascii 0-9, a-z, A-Z, and _ in the name.
238 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '_')
239 return false;
240 }
241
242 return true;
243 }
244
245 // Dictionary interface.
246
247 std::string ExtensionMessageBundle::GetL10nMessage(
248 const std::string& name) const {
249 SubstitutionMap::const_iterator it =
250 dictionary_.find(StringToLowerASCII(name));
251 if (it != dictionary_.end()) {
252 return it->second;
253 }
254
255 return "";
256 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/extension_message_bundle.h ('k') | chrome/browser/extensions/extension_message_bundle_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698