OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/extension_l10n_util.h" | 5 #include "chrome/browser/extensions/extension_l10n_util.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "app/l10n_util.h" | 11 #include "app/l10n_util.h" |
12 #include "base/file_util.h" | 12 #include "base/file_util.h" |
| 13 #include "base/linked_ptr.h" |
13 #include "base/string_util.h" | 14 #include "base/string_util.h" |
14 #include "base/values.h" | 15 #include "base/values.h" |
15 #include "chrome/common/extensions/extension.h" | 16 #include "chrome/common/extensions/extension.h" |
16 #include "chrome/common/extensions/extension_constants.h" | 17 #include "chrome/common/extensions/extension_constants.h" |
17 #include "chrome/common/extensions/extension_message_bundle.h" | 18 #include "chrome/common/extensions/extension_message_bundle.h" |
18 #include "chrome/common/json_value_serializer.h" | 19 #include "chrome/common/json_value_serializer.h" |
19 | 20 |
20 namespace errors = extension_manifest_errors; | 21 namespace errors = extension_manifest_errors; |
21 namespace keys = extension_manifest_keys; | 22 namespace keys = extension_manifest_keys; |
22 | 23 |
23 namespace extension_l10n_util { | 24 namespace extension_l10n_util { |
24 | 25 |
25 std::string GetDefaultLocaleFromManifest(const DictionaryValue& manifest, | 26 std::string GetDefaultLocaleFromManifest(const DictionaryValue& manifest, |
26 std::string* error) { | 27 std::string* error) { |
27 std::string default_locale; | 28 std::string default_locale; |
28 if (!manifest.GetString(keys::kDefaultLocale, &default_locale)) { | 29 if (!manifest.GetString(keys::kDefaultLocale, &default_locale)) { |
29 *error = errors::kInvalidDefaultLocale; | 30 *error = errors::kInvalidDefaultLocale; |
30 return ""; | 31 return ""; |
31 } | 32 } |
32 // Normalize underscores to hyphens. | 33 |
33 std::replace(default_locale.begin(), default_locale.end(), '_', '-'); | |
34 return default_locale; | 34 return default_locale; |
35 } | 35 } |
36 | 36 |
37 bool AddLocale(const std::set<std::string>& chrome_locales, | 37 bool AddLocale(const std::set<std::string>& chrome_locales, |
38 const FilePath& locale_folder, | 38 const FilePath& locale_folder, |
| 39 const std::string& locale_name, |
39 std::set<std::string>* valid_locales, | 40 std::set<std::string>* valid_locales, |
40 std::string* locale_name, | |
41 std::string* error) { | 41 std::string* error) { |
42 // Normalize underscores to hyphens because that's what our locale files use. | |
43 std::replace(locale_name->begin(), locale_name->end(), '_', '-'); | |
44 // Accept name that starts with a . but don't add it to the list of supported | 42 // Accept name that starts with a . but don't add it to the list of supported |
45 // locales. | 43 // locales. |
46 if (locale_name->find(".") == 0) | 44 if (locale_name.find(".") == 0) |
47 return true; | 45 return true; |
48 if (chrome_locales.find(*locale_name) == chrome_locales.end()) { | 46 if (chrome_locales.find(locale_name) == chrome_locales.end()) { |
49 // Fail if there is an extension locale that's not in the Chrome list. | 47 // Fail if there is an extension locale that's not in the Chrome list. |
50 *error = StringPrintf("Supplied locale %s is not supported.", | 48 *error = StringPrintf("Supplied locale %s is not supported.", |
51 locale_name->c_str()); | 49 locale_name.c_str()); |
52 return false; | 50 return false; |
53 } | 51 } |
54 // Check if messages file is actually present (but don't check content). | 52 // Check if messages file is actually present (but don't check content). |
55 if (file_util::PathExists( | 53 if (file_util::PathExists( |
56 locale_folder.AppendASCII(Extension::kMessagesFilename))) { | 54 locale_folder.AppendASCII(Extension::kMessagesFilename))) { |
57 valid_locales->insert(*locale_name); | 55 valid_locales->insert(locale_name); |
58 } else { | 56 } else { |
59 *error = StringPrintf("Catalog file is missing for locale %s.", | 57 *error = StringPrintf("Catalog file is missing for locale %s.", |
60 locale_name->c_str()); | 58 locale_name.c_str()); |
61 return false; | 59 return false; |
62 } | 60 } |
63 | 61 |
64 return true; | 62 return true; |
65 } | 63 } |
66 | 64 |
| 65 // Converts all - into _, to be consistent with ICU and file system names. |
| 66 static std::string NormalizeLocale(const std::string& locale) { |
| 67 std::string normalized_locale(locale); |
| 68 std::replace(normalized_locale.begin(), normalized_locale.end(), '-', '_'); |
| 69 |
| 70 return normalized_locale; |
| 71 } |
| 72 |
| 73 // Produce a vector of parent locales for given locale. |
| 74 // It includes the current locale in the result. |
| 75 // sr_Cyrl_RS generates sr_Cyrl_RS, sr_Cyrl and sr. |
| 76 static void GetParentLocales(const std::string& current_locale, |
| 77 std::vector<std::string>* parent_locales) { |
| 78 std::string locale(NormalizeLocale(current_locale)); |
| 79 |
| 80 const int kNameCapacity = 256; |
| 81 char parent[kNameCapacity]; |
| 82 strncpy(parent, locale.c_str(), kNameCapacity); |
| 83 parent_locales->push_back(parent); |
| 84 UErrorCode err = U_ZERO_ERROR; |
| 85 while (uloc_getParent(parent, parent, kNameCapacity, &err) > 0) { |
| 86 if (err != U_ZERO_ERROR) |
| 87 break; |
| 88 parent_locales->push_back(parent); |
| 89 } |
| 90 } |
| 91 |
| 92 // Extends list of Chrome locales to them and their parents, so we can do |
| 93 // proper fallback. |
| 94 static void GetAllLocales(std::set<std::string>* all_locales) { |
| 95 const std::vector<std::string>& available_locales = |
| 96 l10n_util::GetAvailableLocales(); |
| 97 // Add all parents of the current locale to the available locales set. |
| 98 // I.e. for sr_Cyrl_RS we add sr_Cyrl_RS, sr_Cyrl and sr. |
| 99 for (size_t i = 0; i < available_locales.size(); ++i) { |
| 100 std::vector<std::string> result; |
| 101 GetParentLocales(available_locales[i], &result); |
| 102 all_locales->insert(result.begin(), result.end()); |
| 103 } |
| 104 } |
| 105 |
67 bool GetValidLocales(const FilePath& locale_path, | 106 bool GetValidLocales(const FilePath& locale_path, |
68 std::set<std::string>* valid_locales, | 107 std::set<std::string>* valid_locales, |
69 std::string* error) { | 108 std::string* error) { |
70 // Get available chrome locales as a set. | 109 static std::set<std::string> chrome_locales; |
71 const std::vector<std::string>& available_locales = | 110 GetAllLocales(&chrome_locales); |
72 l10n_util::GetAvailableLocales(); | 111 |
73 static std::set<std::string> chrome_locales(available_locales.begin(), | |
74 available_locales.end()); | |
75 // Enumerate all supplied locales in the extension. | 112 // Enumerate all supplied locales in the extension. |
76 file_util::FileEnumerator locales(locale_path, | 113 file_util::FileEnumerator locales(locale_path, |
77 false, | 114 false, |
78 file_util::FileEnumerator::DIRECTORIES); | 115 file_util::FileEnumerator::DIRECTORIES); |
79 FilePath locale_folder; | 116 FilePath locale_folder; |
80 while (!(locale_folder = locales.Next()).empty()) { | 117 while (!(locale_folder = locales.Next()).empty()) { |
81 std::string locale_name = | 118 std::string locale_name = |
82 WideToASCII(locale_folder.BaseName().ToWStringHack()); | 119 WideToASCII(locale_folder.BaseName().ToWStringHack()); |
83 if (!AddLocale(chrome_locales, | 120 if (!AddLocale(chrome_locales, |
84 locale_folder, | 121 locale_folder, |
| 122 locale_name, |
85 valid_locales, | 123 valid_locales, |
86 &locale_name, | |
87 error)) { | 124 error)) { |
88 return false; | 125 return false; |
89 } | 126 } |
90 } | 127 } |
91 | 128 |
92 if (valid_locales->empty()) { | 129 if (valid_locales->empty()) { |
93 *error = extension_manifest_errors::kLocalesNoValidLocaleNamesListed; | 130 *error = extension_manifest_errors::kLocalesNoValidLocaleNamesListed; |
94 return false; | 131 return false; |
95 } | 132 } |
96 | 133 |
97 return true; | 134 return true; |
98 } | 135 } |
99 | 136 |
100 // Loads contents of the messages file for given locale. If file is not found, | 137 // Loads contents of the messages file for given locale. If file is not found, |
101 // or there was parsing error we return NULL and set |error|. | 138 // or there was parsing error we return NULL and set |error|. |
102 // Caller owns the returned object. | 139 // Caller owns the returned object. |
103 static DictionaryValue* LoadMessageFile(const FilePath& locale_path, | 140 static DictionaryValue* LoadMessageFile(const FilePath& locale_path, |
104 const std::string& locale, | 141 const std::string& locale, |
105 std::string* error) { | 142 std::string* error) { |
| 143 |
106 std::string extension_locale = locale; | 144 std::string extension_locale = locale; |
107 // Normalize hyphens to underscores because that's what our locale files use. | |
108 std::replace(extension_locale.begin(), extension_locale.end(), '-', '_'); | |
109 FilePath file = locale_path.AppendASCII(extension_locale) | 145 FilePath file = locale_path.AppendASCII(extension_locale) |
110 .AppendASCII(Extension::kMessagesFilename); | 146 .AppendASCII(Extension::kMessagesFilename); |
111 JSONFileValueSerializer messages_serializer(file); | 147 JSONFileValueSerializer messages_serializer(file); |
112 Value *dictionary = messages_serializer.Deserialize(error); | 148 Value *dictionary = messages_serializer.Deserialize(error); |
113 if (!dictionary && error->empty()) { | 149 if (!dictionary && error->empty()) { |
114 // JSONFileValueSerializer just returns NULL if file cannot be found. It | 150 // JSONFileValueSerializer just returns NULL if file cannot be found. It |
115 // doesn't set the error, so we have to do it. | 151 // doesn't set the error, so we have to do it. |
116 *error = StringPrintf("Catalog file is missing for locale %s.", | 152 *error = StringPrintf("Catalog file is missing for locale %s.", |
117 extension_locale.c_str()); | 153 extension_locale.c_str()); |
118 } | 154 } |
119 | 155 |
120 return static_cast<DictionaryValue*>(dictionary); | 156 return static_cast<DictionaryValue*>(dictionary); |
121 } | 157 } |
122 | 158 |
123 ExtensionMessageBundle* LoadMessageCatalogs( | 159 ExtensionMessageBundle* LoadMessageCatalogs( |
124 const FilePath& locale_path, | 160 const FilePath& locale_path, |
125 const std::string& default_locale, | 161 const std::string& default_locale, |
126 const std::string& application_locale, | 162 const std::string& application_locale, |
| 163 const std::set<std::string>& valid_locales, |
127 std::string* error) { | 164 std::string* error) { |
128 scoped_ptr<DictionaryValue> default_catalog( | 165 // Order locales to load as current_locale, first_parent, ..., default_locale. |
129 LoadMessageFile(locale_path, default_locale, error)); | 166 std::vector<std::string> all_fallback_locales; |
130 if (!default_catalog.get()) { | 167 if (!application_locale.empty() && application_locale != default_locale) |
131 return false; | 168 GetParentLocales(application_locale, &all_fallback_locales); |
| 169 all_fallback_locales.push_back(default_locale); |
| 170 |
| 171 std::vector<linked_ptr<DictionaryValue> > catalogs; |
| 172 for (size_t i = 0; i < all_fallback_locales.size(); ++i) { |
| 173 // Skip all parent locales that are not supplied. |
| 174 if (valid_locales.find(all_fallback_locales[i]) == valid_locales.end()) |
| 175 continue; |
| 176 linked_ptr<DictionaryValue> catalog( |
| 177 LoadMessageFile(locale_path, all_fallback_locales[i], error)); |
| 178 if (!catalog.get()) { |
| 179 // If locale is valid, but messages.json is corrupted or missing, return |
| 180 // an error. |
| 181 return false; |
| 182 } else { |
| 183 catalogs.push_back(catalog); |
| 184 } |
132 } | 185 } |
133 | 186 |
134 scoped_ptr<DictionaryValue> app_catalog( | 187 return ExtensionMessageBundle::Create(catalogs, error); |
135 LoadMessageFile(locale_path, application_locale, error)); | |
136 if (!app_catalog.get()) { | |
137 // Only default catalog has to be present. This is not an error. | |
138 app_catalog.reset(new DictionaryValue); | |
139 error->clear(); | |
140 } | |
141 | |
142 return ExtensionMessageBundle::Create(*default_catalog, | |
143 *app_catalog, | |
144 error); | |
145 } | 188 } |
146 | 189 |
147 FilePath GetL10nRelativePath(const FilePath& relative_resource_path) { | 190 FilePath GetL10nRelativePath(const FilePath& relative_resource_path) { |
148 // Get locale relative path. | 191 // Get locale relative path. |
149 static std::string current_locale = l10n_util::GetApplicationLocale(L""); | 192 static std::string current_locale = l10n_util::GetApplicationLocale(L""); |
150 std::replace(current_locale.begin(), current_locale.end(), '-', '_'); | 193 std::replace(current_locale.begin(), current_locale.end(), '-', '_'); |
151 | 194 |
152 FilePath locale_relative_path; | 195 FilePath locale_relative_path; |
153 return locale_relative_path | 196 return locale_relative_path |
154 .AppendASCII(Extension::kLocaleFolder) | 197 .AppendASCII(Extension::kLocaleFolder) |
155 .AppendASCII(current_locale) | 198 .AppendASCII(current_locale) |
156 .Append(relative_resource_path); | 199 .Append(relative_resource_path); |
157 } | 200 } |
158 | 201 |
159 } // namespace extension_l10n_util | 202 } // namespace extension_l10n_util |
OLD | NEW |