1// Copyright (C) 2013 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <libaddressinput/localization.h>
16
17#include <libaddressinput/address_data.h>
18#include <libaddressinput/address_field.h>
19#include <libaddressinput/address_problem.h>
20
21#include <cassert>
22#include <cstddef>
23#include <string>
24#include <vector>
25
26#include "messages.h"
27#include "region_data_constants.h"
28#include "rule.h"
29#include "util/string_split.h"
30#include "util/string_util.h"
31
32namespace {
33
34void PushBackUrl(const std::string& url, std::vector<std::string>* parameters) {
35  assert(parameters != NULL);
36  // TODO: HTML-escape the "url".
37  parameters->push_back("<a href=\"" + url + "\">");
38  parameters->push_back("</a>");
39}
40
41}  // namespace
42
43namespace i18n {
44namespace addressinput {
45
46namespace {
47
48#include "en_messages.cc"
49
50std::string GetEnglishString(int message_id) {
51  const char* str = GetString(message_id);
52  return str != NULL ? std::string(str) : std::string();
53}
54
55}  // namespace
56
57Localization::Localization() : get_string_(&GetEnglishString) {}
58
59Localization::~Localization() {}
60
61std::string Localization::GetString(int message_id) const {
62  return get_string_(message_id);
63}
64
65std::string Localization::GetErrorMessage(const AddressData& address,
66                                          AddressField field,
67                                          AddressProblem problem,
68                                          bool enable_examples,
69                                          bool enable_links) const {
70  if (field == POSTAL_CODE) {
71    Rule rule;
72    rule.CopyFrom(Rule::GetDefault());
73    std::string postal_code_example, post_service_url;
74    if (rule.ParseSerializedRule(
75            RegionDataConstants::GetRegionData(address.region_code))) {
76      if (enable_examples) {
77        std::vector<std::string> examples_list;
78        SplitString(rule.GetPostalCodeExample(), ',', &examples_list);
79        if (!examples_list.empty()) {
80          postal_code_example = examples_list.front();
81        }
82      }
83      if (enable_links) {
84        post_service_url = rule.GetPostServiceUrl();
85      }
86    } else {
87      assert(false);
88    }
89    // If we can't parse the serialized rule |uses_postal_code_as_label| will be
90    // determined from the default rule.
91    bool uses_postal_code_as_label =
92        rule.GetPostalCodeNameMessageId() ==
93        IDS_LIBADDRESSINPUT_POSTAL_CODE_LABEL;
94    return GetErrorMessageForPostalCode(address, problem,
95                                        uses_postal_code_as_label,
96                                        postal_code_example, post_service_url);
97  } else {
98    if (problem == MISSING_REQUIRED_FIELD) {
99      return get_string_(IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD);
100    } else if (problem == UNKNOWN_VALUE) {
101      std::vector<std::string> parameters;
102      if (AddressData::IsRepeatedFieldValue(field)) {
103        std::vector<std::string> values = address.GetRepeatedFieldValue(field);
104        assert(!values.empty());
105        parameters.push_back(values.front());
106      } else {
107        parameters.push_back(address.GetFieldValue(field));
108      }
109      return DoReplaceStringPlaceholders(
110          get_string_(IDS_LIBADDRESSINPUT_UNKNOWN_VALUE), parameters);
111    } else if (problem == USES_P_O_BOX) {
112      return get_string_(IDS_LIBADDRESSINPUT_PO_BOX_FORBIDDEN_VALUE);
113    } else {
114      // Keep the default under "else" so the compiler helps us check that all
115      // handled cases return and don't fall through.
116      assert(false);
117      return "";
118    }
119  }
120}
121
122void Localization::SetGetter(std::string (*getter)(int)) {
123  assert(getter != NULL);
124  get_string_ = getter;
125}
126
127std::string Localization::GetErrorMessageForPostalCode(
128    const AddressData& address,
129    AddressProblem problem,
130    bool uses_postal_code_as_label,
131    std::string postal_code_example,
132    std::string post_service_url) const {
133  int message_id;
134  std::vector<std::string> parameters;
135  if (problem == MISSING_REQUIRED_FIELD) {
136    if (!postal_code_example.empty() && !post_service_url.empty()) {
137      message_id = uses_postal_code_as_label ?
138          IDS_LIBADDRESSINPUT_MISSING_REQUIRED_POSTAL_CODE_EXAMPLE_AND_URL :
139          IDS_LIBADDRESSINPUT_MISSING_REQUIRED_ZIP_CODE_EXAMPLE_AND_URL;
140      parameters.push_back(postal_code_example);
141      PushBackUrl(post_service_url, &parameters);
142    } else if (!postal_code_example.empty()) {
143      message_id = uses_postal_code_as_label ?
144          IDS_LIBADDRESSINPUT_MISSING_REQUIRED_POSTAL_CODE_EXAMPLE :
145          IDS_LIBADDRESSINPUT_MISSING_REQUIRED_ZIP_CODE_EXAMPLE ;
146      parameters.push_back(postal_code_example);
147    } else {
148      message_id = IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD;
149    }
150    return DoReplaceStringPlaceholders(get_string_(message_id), parameters);
151  } else if (problem == INVALID_FORMAT) {
152    if (!postal_code_example.empty() && !post_service_url.empty()) {
153      message_id = uses_postal_code_as_label ?
154          IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_POSTAL_CODE_EXAMPLE_AND_URL :
155          IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP_CODE_EXAMPLE_AND_URL;
156      parameters.push_back(postal_code_example);
157      PushBackUrl(post_service_url, &parameters);
158    } else if (!postal_code_example.empty()) {
159      message_id = uses_postal_code_as_label ?
160          IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_POSTAL_CODE_EXAMPLE :
161          IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP_CODE_EXAMPLE;
162      parameters.push_back(postal_code_example);
163    } else {
164      message_id = uses_postal_code_as_label ?
165          IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_POSTAL_CODE :
166          IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP;
167    }
168    return DoReplaceStringPlaceholders(get_string_(message_id), parameters);
169  } else if (problem == MISMATCHING_VALUE) {
170    if (!post_service_url.empty()) {
171      message_id = uses_postal_code_as_label ?
172          IDS_LIBADDRESSINPUT_MISMATCHING_VALUE_POSTAL_CODE_URL :
173          IDS_LIBADDRESSINPUT_MISMATCHING_VALUE_ZIP_URL;
174      PushBackUrl(post_service_url, &parameters);
175    } else {
176      message_id = uses_postal_code_as_label ?
177          IDS_LIBADDRESSINPUT_MISMATCHING_VALUE_POSTAL_CODE :
178          IDS_LIBADDRESSINPUT_MISMATCHING_VALUE_ZIP;
179    }
180    return DoReplaceStringPlaceholders(get_string_(message_id), parameters);
181  } else {
182    // Keep the default under "else" so the compiler helps us check that all
183    // handled cases return and don't fall through.
184    assert(false);
185    return "";
186  }
187}
188
189}  // namespace addressinput
190}  // namespace i18n
191