1 // Copyright 2015 The Chromium Authors
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 "base/i18n/message_formatter.h"
6
7 #include "base/check.h"
8 #include "base/i18n/unicodestring.h"
9 #include "base/logging.h"
10 #include "base/numerics/safe_conversions.h"
11 #include "base/time/time.h"
12 #include "third_party/icu/source/common/unicode/unistr.h"
13 #include "third_party/icu/source/common/unicode/utypes.h"
14 #include "third_party/icu/source/i18n/unicode/fmtable.h"
15 #include "third_party/icu/source/i18n/unicode/msgfmt.h"
16
17 using icu::UnicodeString;
18
19 namespace base {
20 namespace i18n {
21 namespace {
UnicodeStringFromStringPiece(StringPiece str)22 UnicodeString UnicodeStringFromStringPiece(StringPiece str) {
23 return UnicodeString::fromUTF8(
24 icu::StringPiece(str.data(), base::checked_cast<int32_t>(str.size())));
25 }
26 } // anonymous namespace
27
28 namespace internal {
MessageArg()29 MessageArg::MessageArg() : formattable(nullptr) {}
30
MessageArg(const char * s)31 MessageArg::MessageArg(const char* s)
32 : formattable(new icu::Formattable(UnicodeStringFromStringPiece(s))) {}
33
MessageArg(StringPiece s)34 MessageArg::MessageArg(StringPiece s)
35 : formattable(new icu::Formattable(UnicodeStringFromStringPiece(s))) {}
36
MessageArg(const std::string & s)37 MessageArg::MessageArg(const std::string& s)
38 : formattable(new icu::Formattable(UnicodeString::fromUTF8(s))) {}
39
MessageArg(const std::u16string & s)40 MessageArg::MessageArg(const std::u16string& s)
41 : formattable(new icu::Formattable(UnicodeString(s.data(), s.size()))) {}
42
MessageArg(int i)43 MessageArg::MessageArg(int i) : formattable(new icu::Formattable(i)) {}
44
MessageArg(int64_t i)45 MessageArg::MessageArg(int64_t i) : formattable(new icu::Formattable(i)) {}
46
MessageArg(double d)47 MessageArg::MessageArg(double d) : formattable(new icu::Formattable(d)) {}
48
MessageArg(const Time & t)49 MessageArg::MessageArg(const Time& t)
50 : formattable(new icu::Formattable(
51 static_cast<UDate>(t.InMillisecondsFSinceUnixEpoch()))) {}
52
53 MessageArg::~MessageArg() = default;
54
55 // Tests if this argument has a value, and if so increments *count.
has_value(int * count) const56 bool MessageArg::has_value(int *count) const {
57 if (formattable == nullptr)
58 return false;
59
60 ++*count;
61 return true;
62 }
63
64 } // namespace internal
65
FormatWithNumberedArgs(StringPiece16 msg,const internal::MessageArg & arg0,const internal::MessageArg & arg1,const internal::MessageArg & arg2,const internal::MessageArg & arg3,const internal::MessageArg & arg4,const internal::MessageArg & arg5,const internal::MessageArg & arg6)66 std::u16string MessageFormatter::FormatWithNumberedArgs(
67 StringPiece16 msg,
68 const internal::MessageArg& arg0,
69 const internal::MessageArg& arg1,
70 const internal::MessageArg& arg2,
71 const internal::MessageArg& arg3,
72 const internal::MessageArg& arg4,
73 const internal::MessageArg& arg5,
74 const internal::MessageArg& arg6) {
75 int32_t args_count = 0;
76 icu::Formattable args[] = {
77 arg0.has_value(&args_count) ? *arg0.formattable : icu::Formattable(),
78 arg1.has_value(&args_count) ? *arg1.formattable : icu::Formattable(),
79 arg2.has_value(&args_count) ? *arg2.formattable : icu::Formattable(),
80 arg3.has_value(&args_count) ? *arg3.formattable : icu::Formattable(),
81 arg4.has_value(&args_count) ? *arg4.formattable : icu::Formattable(),
82 arg5.has_value(&args_count) ? *arg5.formattable : icu::Formattable(),
83 arg6.has_value(&args_count) ? *arg6.formattable : icu::Formattable(),
84 };
85
86 UnicodeString msg_string(msg.data(), msg.size());
87 UErrorCode error = U_ZERO_ERROR;
88 icu::MessageFormat format(msg_string, error);
89 icu::UnicodeString formatted;
90 icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
91 format.format(args, args_count, formatted, ignore, error);
92 if (U_FAILURE(error)) {
93 LOG(ERROR) << "MessageFormat(" << msg << ") failed with "
94 << u_errorName(error);
95 return std::u16string();
96 }
97 return i18n::UnicodeStringToString16(formatted);
98 }
99
FormatWithNamedArgs(StringPiece16 msg,StringPiece name0,const internal::MessageArg & arg0,StringPiece name1,const internal::MessageArg & arg1,StringPiece name2,const internal::MessageArg & arg2,StringPiece name3,const internal::MessageArg & arg3,StringPiece name4,const internal::MessageArg & arg4,StringPiece name5,const internal::MessageArg & arg5,StringPiece name6,const internal::MessageArg & arg6)100 std::u16string MessageFormatter::FormatWithNamedArgs(
101 StringPiece16 msg,
102 StringPiece name0,
103 const internal::MessageArg& arg0,
104 StringPiece name1,
105 const internal::MessageArg& arg1,
106 StringPiece name2,
107 const internal::MessageArg& arg2,
108 StringPiece name3,
109 const internal::MessageArg& arg3,
110 StringPiece name4,
111 const internal::MessageArg& arg4,
112 StringPiece name5,
113 const internal::MessageArg& arg5,
114 StringPiece name6,
115 const internal::MessageArg& arg6) {
116 icu::UnicodeString names[] = {
117 UnicodeStringFromStringPiece(name0),
118 UnicodeStringFromStringPiece(name1),
119 UnicodeStringFromStringPiece(name2),
120 UnicodeStringFromStringPiece(name3),
121 UnicodeStringFromStringPiece(name4),
122 UnicodeStringFromStringPiece(name5),
123 UnicodeStringFromStringPiece(name6),
124 };
125 int32_t args_count = 0;
126 icu::Formattable args[] = {
127 arg0.has_value(&args_count) ? *arg0.formattable : icu::Formattable(),
128 arg1.has_value(&args_count) ? *arg1.formattable : icu::Formattable(),
129 arg2.has_value(&args_count) ? *arg2.formattable : icu::Formattable(),
130 arg3.has_value(&args_count) ? *arg3.formattable : icu::Formattable(),
131 arg4.has_value(&args_count) ? *arg4.formattable : icu::Formattable(),
132 arg5.has_value(&args_count) ? *arg5.formattable : icu::Formattable(),
133 arg6.has_value(&args_count) ? *arg6.formattable : icu::Formattable(),
134 };
135
136 UnicodeString msg_string(msg.data(), msg.size());
137 UErrorCode error = U_ZERO_ERROR;
138 icu::MessageFormat format(msg_string, error);
139
140 icu::UnicodeString formatted;
141 format.format(names, args, args_count, formatted, error);
142 if (U_FAILURE(error)) {
143 LOG(ERROR) << "MessageFormat(" << msg << ") failed with "
144 << u_errorName(error);
145 return std::u16string();
146 }
147 return i18n::UnicodeStringToString16(formatted);
148 }
149
150 } // namespace i18n
151 } // namespace base
152