diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormBaseActivity.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormBaseActivity.java index acf0dd9fa..fdebc5ed7 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormBaseActivity.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormBaseActivity.java @@ -33,6 +33,8 @@ import java.util.List; import java.util.Map; +import static com.vijay.jsonwizard.utils.NativeFormLangUtils.getTranslatedString; + abstract class JsonFormBaseActivity extends MultiLanguageActivity implements OnFieldsInvalid { protected static final String TAG = JsonFormActivity.class.getSimpleName(); protected static final String JSON_STATE = "jsonState"; @@ -69,7 +71,7 @@ protected void onCreate(Bundle savedInstanceState) { lifeCycleListeners = new ArrayList<>(); isFormFragmentInitialized = false; if (savedInstanceState == null) { - init(getIntent().getStringExtra(JsonFormConstants.JSON_FORM_KEY.JSON)); + init(getForm()); initializeFormFragment(); onFormStart(); this.form = extractForm(getIntent().getSerializableExtra(JsonFormConstants.JSON_FORM_KEY.FORM)); @@ -82,6 +84,14 @@ protected void onCreate(Bundle savedInstanceState) { } } + private String getForm() { + String jsonForm = getIntent().getStringExtra(JsonFormConstants.JSON_FORM_KEY.JSON); + if (getIntent().getBooleanExtra(JsonFormConstants.PERFORM_FORM_TRANSLATION, false)) { + jsonForm = getTranslatedString(jsonForm); + } + return jsonForm; + } + public void init(String json) { try { setmJSONObject(new JSONObject(json)); diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/constants/JsonFormConstants.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/constants/JsonFormConstants.java index 529cb0670..585e5dd39 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/constants/JsonFormConstants.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/constants/JsonFormConstants.java @@ -207,13 +207,14 @@ public class JsonFormConstants { public static final String FALSE = "false"; public static final String DISPLAY_SCROLL_BARS = "display_scroll_bars"; public static final String SKIP_BLANK_STEPS = "skip_blank_steps"; - public static final String MULTI_SELECT_LIST ="multi_select_list" ; + public static final String MULTI_SELECT_LIST = "multi_select_list"; public static final String FORM_VERSION = "form_version"; public static final String LABEL_CONSTRAINT_LAYOUT = "label_constraint_layout"; public static final String STEP = "step"; public static final String RULE = "rule/"; public static final String REFERENCE_EDIT_TEXT = "reference_edit_text"; public static final String DISPLAY_LABEL = "display_label"; + public static final String PERFORM_FORM_TRANSLATION = "perform_form_translation"; public interface MultiSelectUtils { String IS_HEADER = "isHeader"; diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interactors/JsonFormInteractor.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interactors/JsonFormInteractor.java index 91fadad1c..ca66bea1b 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interactors/JsonFormInteractor.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interactors/JsonFormInteractor.java @@ -68,7 +68,6 @@ public JsonFormInteractor(@Nullable Map additionalWid } } - public static JsonFormInteractor getInstance(@Nullable Map additionalWidgetsMap) { if (INSTANCE == null) { INSTANCE = new JsonFormInteractor(additionalWidgetsMap); diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/FormWidgetFactory.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/FormWidgetFactory.java index 4872ec7fb..f4207de53 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/FormWidgetFactory.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/FormWidgetFactory.java @@ -13,6 +13,7 @@ * Created by vijay on 24-05-2015. */ public interface FormWidgetFactory { + List getViewsFromJson(String stepName, Context context, JsonFormFragment formFragment, JSONObject jsonObject, CommonListener listener, boolean popup) throws Exception; List getViewsFromJson(String stepName, Context context, JsonFormFragment formFragment, JSONObject jsonObject, CommonListener listener) throws Exception; diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/NativeFormLangUtils.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/NativeFormLangUtils.java index ac7bc8c38..66f0906d8 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/NativeFormLangUtils.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/NativeFormLangUtils.java @@ -7,6 +7,11 @@ import android.util.Log; import java.util.Locale; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import timber.log.Timber; public class NativeFormLangUtils { @@ -31,4 +36,44 @@ public static Context setAppLocale(Context ctx, String language) { return context; } + + /** + * Performs translation on an interpolated {@param str} + * i.e. a String containing tokens in the format {{string_name}}, + * replacing these tokens with their corresponding values for the current Locale + * + * @param str + * @return + */ + public static String getTranslatedString(String str) { + String translationsFileName = getTranslationsFileName(str); + if (translationsFileName.isEmpty()) { + Timber.e("Could not translate the String. Translation file name is not specified!"); + return str; + } + + ResourceBundle mlsResourceBundle = ResourceBundle.getBundle(getTranslationsFileName(str)); + + StringBuffer stringBuffer = new StringBuffer(); + Pattern interpolatedStringPattern = Pattern.compile("\\{\\{([a-zA-Z_0-9\\.]+)\\}\\}"); + Matcher matcher = interpolatedStringPattern.matcher(str); + while (matcher.find()) { + matcher.appendReplacement(stringBuffer, mlsResourceBundle.getString(matcher.group(1))); + } + matcher.appendTail(stringBuffer); + + return stringBuffer.toString(); + } + + /** + * Gets the name of the translation file to be applied to the {@param str} + * + * @param str + * @return + */ + public static String getTranslationsFileName(String str) { + Pattern propertiesFileNamePattern = Pattern.compile("\"?properties_file_name\"?: ?\"([a-zA-Z_0-9\\.]+)\""); + Matcher matcher = propertiesFileNamePattern.matcher(str); + return matcher.find() ? matcher.group(1) : ""; + } } diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/Utils.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/Utils.java index 89310006b..1552c3320 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/Utils.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/Utils.java @@ -40,7 +40,6 @@ import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; @@ -52,12 +51,14 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Scanner; import java.util.Set; import java.util.concurrent.TimeUnit; import timber.log.Timber; import static com.vijay.jsonwizard.constants.JsonFormConstants.KEY; +import static com.vijay.jsonwizard.utils.NativeFormLangUtils.getTranslatedString; public class Utils { private static ProgressDialog progressDialog; @@ -482,11 +483,10 @@ private static String cleanConditionString(String conditionStringRaw) { public static Iterable readYamlFile(String fileName, Context context) { Yaml yaml = new Yaml(); - InputStreamReader inputStreamReader; Iterable objectIterable = null; try { - inputStreamReader = new InputStreamReader(context.getAssets().open(fileName)); - objectIterable = yaml.loadAll(inputStreamReader); + String translatedYamlStr = getTranslatedYamlFile(fileName, context); + objectIterable = yaml.loadAll(translatedYamlStr); } catch (IOException e) { Timber.e(e); } @@ -494,6 +494,48 @@ public static Iterable readYamlFile(String fileName, Context context) { return objectIterable; } + /** + * Translates a yaml file specified by {@param fileName} and returns its String representation + * + * @param fileName + * @param context + * + * @return Translated Yaml file in its String representation + * + * @throws IOException + */ + public static String getTranslatedYamlFile(String fileName, Context context) throws IOException { + return getTranslatedString(getAssetFileAsString(fileName, context)); + } + + /** + * + * Gets a file specified by {@param fileName} from the assets folder as a String + * + * @param fileName + * @param context + * + * @return A file from the assets folder as a String + * + * @throws IOException + */ + public static String getAssetFileAsString(String fileName, Context context) throws IOException { + InputStream inputStream = context.getAssets().open(fileName); + return convertStreamToString(inputStream); + } + + /** + * Converts an {@link InputStream} into a String + * + * @param inputStream + * + * @return String representation of an {@link InputStream} + */ + public static String convertStreamToString(InputStream inputStream) { + Scanner s = new Scanner(inputStream).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } + public static void buildRulesWithUniqueId(JSONObject element, String uniqueId, String ruleType, WidgetArgs widgetArgs, Map>> rulesFileMap) throws JSONException { JSONObject rules = element.optJSONObject(ruleType); if (rules != null) { diff --git a/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/NativeFormLangUtilsTest.java b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/NativeFormLangUtilsTest.java new file mode 100644 index 000000000..71f8a2400 --- /dev/null +++ b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/NativeFormLangUtilsTest.java @@ -0,0 +1,76 @@ +package com.vijay.jsonwizard.utils; + +import android.content.Context; +import android.content.res.AssetManager; + +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; + +import static com.vijay.jsonwizard.utils.Utils.convertStreamToString; +import static com.vijay.jsonwizard.utils.Utils.getTranslatedYamlFile; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +/** + * Created by Vincent Karuri on 20/02/2020 + */ +public class NativeFormLangUtilsTest { + + @Test + public void testJsonFormTranslationShouldTranslateForm() { + Locale.setDefault(new Locale("id")); + String expectedJsonForm = getFileContentsAsString("test_form_translation_in"); + String interpolatedJsonForm = getFileContentsAsString("test_form_translation_interpolated"); + assertEquals(expectedJsonForm, NativeFormLangUtils.getTranslatedString(interpolatedJsonForm)); + + Locale.setDefault(new Locale("en", "US")); + expectedJsonForm = getFileContentsAsString("test_form_translation_en_US"); + assertEquals(expectedJsonForm, NativeFormLangUtils.getTranslatedString(interpolatedJsonForm)); + } + + @Test + public void testYamlFileTranslationShouldTranslateYamlFile() throws IOException { + Context context = mockAssetManager("test_yaml_translation_interpolated"); + Locale.setDefault(new Locale("en", "US")); + String translatedYamlStr = getTranslatedYamlFile("file_name", context); + assertEquals(getFileContentsAsString("test_yaml_translation_en_US"), translatedYamlStr); + } + + @Test + public void testJsonFormTranslationShouldReturnUntranslatedForm() { + Locale.setDefault(new Locale("id")); + String interpolatedJsonForm = getFileContentsAsString("test_form_translation_interpolated_missing_translations"); + assertEquals(interpolatedJsonForm, NativeFormLangUtils.getTranslatedString(interpolatedJsonForm)); + } + + @Test + public void testYamlFileTranslationShouldReturnUntranslatedYamlFile() throws IOException { + Context context = mockAssetManager("test_yaml_translation_interpolated_missing_translations"); + Locale.setDefault(new Locale("en", "US")); + String translatedYamlStr = getTranslatedYamlFile("file_name", context); + assertEquals(getFileContentsAsString("test_yaml_translation_interpolated_missing_translations"), translatedYamlStr); + } + + private Context mockAssetManager(String yamlFilePath) throws IOException { + Context context = mock(Context.class); + AssetManager assetManager = mock(AssetManager.class); + doReturn(getTestResource(yamlFilePath)) + .when(assetManager) + .open(eq("file_name")); + doReturn(assetManager).when(context).getAssets(); + return context; + } + + private InputStream getTestResource(String filePath) { + return getClass().getClassLoader().getResourceAsStream(filePath); + } + + private String getFileContentsAsString(String filePath) { + return convertStreamToString(getTestResource(filePath)); + } +} diff --git a/android-json-form-wizard/src/test/resources/form_strings_en_US.properties b/android-json-form-wizard/src/test/resources/form_strings_en_US.properties new file mode 100644 index 000000000..0af12d9bf --- /dev/null +++ b/android-json-form-wizard/src/test/resources/form_strings_en_US.properties @@ -0,0 +1,16 @@ +step1.title = New client record +step1.previous_label = SAVE AND EXIT +step1.submit_label = SAVE +step1.patient_name_label.text = Name +step1.sex.label = Gender +step1.sex.options.Female.text = Female +step1.sex.options.Male.text = Male +step1.sex.v_required.err = Please specify gender +step1.patient_dob.hint = Patient's date of birth +step1.patient_dob.duration.label = Age +step1.patient_occupation_label.text = Occupation +step1.been_treated.label = Has been treated for malaria in the past 3 months? +step1.been_treated.options.Yes.text = Yes +step1.been_treated.options.No.text = No +step1.been_treated.options.not_answered.text = Not Answered +step1.been_treated.v_required.err = Has the patient been treated for malaria in the past 3 months? \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/form_strings_in.properties b/android-json-form-wizard/src/test/resources/form_strings_in.properties new file mode 100644 index 000000000..b12a28c3f --- /dev/null +++ b/android-json-form-wizard/src/test/resources/form_strings_in.properties @@ -0,0 +1,16 @@ +step1.title = Data Pasien Baru +step1.previous_label = SIMPAN DAN KELUAR +step1.submit_label = SIMPAN +step1.patient_name_label.text = Nama +step1.sex.label = Jenis kelamin pasien +step1.sex.options.Female.text = Perempuan +step1.sex.options.Male.text = Laki-laki +step1.sex.v_required.err = Silakan sebutkan jenis kelamin +step1.patient_dob.hint = Tanggal lahir pasien +step1.patient_dob.duration.label = Usia +step1.patient_occupation_label.text = Pekerjaan pasien +step1.been_treated.label = Pernah dirawat karena Malaria dalam 3 bulan terakhir? +step1.been_treated.options.Yes.text = Ya +step1.been_treated.options.No.text = Tidak +step1.been_treated.options.not_answered.text = Tidak dijawab +step1.been_treated.v_required.err = Pernahkah pasien dirawat karena malaria dalam 3 bulan terakhir? \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/test_form_translation_en_US b/android-json-form-wizard/src/test/resources/test_form_translation_en_US new file mode 100644 index 000000000..c871d137e --- /dev/null +++ b/android-json-form-wizard/src/test/resources/test_form_translation_en_US @@ -0,0 +1,180 @@ +{ + "count": "1", + "encounter_type": "patient_registration", + "entity_id": "", + "properties_file_name": "form_strings", + "metadata": { + "start": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "start", + "openmrs_entity_id": "163137AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "end": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "end", + "openmrs_entity_id": "163138AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "today": { + "openmrs_entity_parent": "", + "openmrs_entity": "encounter", + "openmrs_entity_id": "encounter_date" + }, + "deviceid": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "deviceid", + "openmrs_entity_id": "163149AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "subscriberid": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "subscriberid", + "openmrs_entity_id": "163150AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "simserial": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "simserial", + "openmrs_entity_id": "163151AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "phonenumber": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "phonenumber", + "openmrs_entity_id": "163152AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "encounter_location": "" + }, + "step1": { + "title": "New client record", + "display_back_button": "true", + "previous_label": "SAVE AND EXIT", + "bottom_navigation": "true", + "bottom_navigation_orientation": "vertical", + "next_type": "submit", + "submit_label": "SAVE", + "next_form": "json.form/patient-registration-form.json", + "fields": [ + { + "key": "patient_name_label", + "type": "label", + "text": "Name", + "text_color": "#000000" + }, + { + "key": "patient_name", + "openmrs_entity_parent": "", + "openmrs_entity": "person", + "openmrs_entity_id": "first_name", + "type": "edit_text", + "edit_type": "name" + }, + { + "key": "patient_id_label", + "type": "label", + "text": "Patient ID", + "text_color": "#000000" + }, + { + "key": "patient_id", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "edit_text", + "v_required": { + "value": "true", + "err": "Please enter patient ID" + } + }, + { + "key": "sex", + "openmrs_entity_parent": "", + "openmrs_entity": "person", + "openmrs_entity_id": "gender", + "type": "native_radio", + "label": "Gender", + "options": [ + { + "key": "Female", + "text": "Female" + }, + { + "key": "Male", + "text": "Male" + } + ], + "v_required": { + "value": "true", + "err": "Please specify gender" + } + }, + { + "key": "patient_dob", + "openmrs_entity_parent": "", + "openmrs_entity": "person_attribute", + "openmrs_entity_id": "birthdate", + "type": "date_picker", + "hint": "Patient's date of birth", + "expanded": false, + "duration": { + "label": "Age" + }, + "min_date": "today-100y", + "max_date": "today" + }, + { + "key": "age", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "hidden", + "value": "" + }, + { + "key": "patient_occupation_label", + "type": "label", + "text": "Occupation", + "text_color": "#000000" + }, + { + "key": "patient_occupation", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "edit_text", + "v_required": { + "value": "false", + "err": "" + } + }, + { + "key": "been_treated", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "native_radio", + "label": "Has been treated for malaria in the past 3 months?", + "options": [ + { + "key": "Yes", + "text": "Yes" + }, + { + "key": "No", + "text": "No" + }, + { + "key": "Not Answered", + "text": "Not Answered" + } + ], + "v_required": { + "value": "true", + "err": "Has the patient been treated for malaria in the past 3 months?" + } + } + ] + } +} \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/test_form_translation_in b/android-json-form-wizard/src/test/resources/test_form_translation_in new file mode 100644 index 000000000..8a206c188 --- /dev/null +++ b/android-json-form-wizard/src/test/resources/test_form_translation_in @@ -0,0 +1,180 @@ +{ + "count": "1", + "encounter_type": "patient_registration", + "entity_id": "", + "properties_file_name": "form_strings", + "metadata": { + "start": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "start", + "openmrs_entity_id": "163137AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "end": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "end", + "openmrs_entity_id": "163138AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "today": { + "openmrs_entity_parent": "", + "openmrs_entity": "encounter", + "openmrs_entity_id": "encounter_date" + }, + "deviceid": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "deviceid", + "openmrs_entity_id": "163149AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "subscriberid": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "subscriberid", + "openmrs_entity_id": "163150AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "simserial": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "simserial", + "openmrs_entity_id": "163151AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "phonenumber": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "phonenumber", + "openmrs_entity_id": "163152AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "encounter_location": "" + }, + "step1": { + "title": "Data Pasien Baru", + "display_back_button": "true", + "previous_label": "SIMPAN DAN KELUAR", + "bottom_navigation": "true", + "bottom_navigation_orientation": "vertical", + "next_type": "submit", + "submit_label": "SIMPAN", + "next_form": "json.form/patient-registration-form.json", + "fields": [ + { + "key": "patient_name_label", + "type": "label", + "text": "Nama", + "text_color": "#000000" + }, + { + "key": "patient_name", + "openmrs_entity_parent": "", + "openmrs_entity": "person", + "openmrs_entity_id": "first_name", + "type": "edit_text", + "edit_type": "name" + }, + { + "key": "patient_id_label", + "type": "label", + "text": "Patient ID", + "text_color": "#000000" + }, + { + "key": "patient_id", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "edit_text", + "v_required": { + "value": "true", + "err": "Please enter patient ID" + } + }, + { + "key": "sex", + "openmrs_entity_parent": "", + "openmrs_entity": "person", + "openmrs_entity_id": "gender", + "type": "native_radio", + "label": "Jenis kelamin pasien", + "options": [ + { + "key": "Female", + "text": "Perempuan" + }, + { + "key": "Male", + "text": "Laki-laki" + } + ], + "v_required": { + "value": "true", + "err": "Silakan sebutkan jenis kelamin" + } + }, + { + "key": "patient_dob", + "openmrs_entity_parent": "", + "openmrs_entity": "person_attribute", + "openmrs_entity_id": "birthdate", + "type": "date_picker", + "hint": "Tanggal lahir pasien", + "expanded": false, + "duration": { + "label": "Usia" + }, + "min_date": "today-100y", + "max_date": "today" + }, + { + "key": "age", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "hidden", + "value": "" + }, + { + "key": "patient_occupation_label", + "type": "label", + "text": "Pekerjaan pasien", + "text_color": "#000000" + }, + { + "key": "patient_occupation", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "edit_text", + "v_required": { + "value": "false", + "err": "" + } + }, + { + "key": "been_treated", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "native_radio", + "label": "Pernah dirawat karena Malaria dalam 3 bulan terakhir?", + "options": [ + { + "key": "Yes", + "text": "Ya" + }, + { + "key": "No", + "text": "Tidak" + }, + { + "key": "Not Answered", + "text": "Tidak dijawab" + } + ], + "v_required": { + "value": "true", + "err": "Pernahkah pasien dirawat karena malaria dalam 3 bulan terakhir?" + } + } + ] + } +} \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/test_form_translation_interpolated b/android-json-form-wizard/src/test/resources/test_form_translation_interpolated new file mode 100644 index 000000000..1f15b80ec --- /dev/null +++ b/android-json-form-wizard/src/test/resources/test_form_translation_interpolated @@ -0,0 +1,180 @@ +{ + "count": "1", + "encounter_type": "patient_registration", + "entity_id": "", + "properties_file_name": "form_strings", + "metadata": { + "start": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "start", + "openmrs_entity_id": "163137AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "end": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "end", + "openmrs_entity_id": "163138AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "today": { + "openmrs_entity_parent": "", + "openmrs_entity": "encounter", + "openmrs_entity_id": "encounter_date" + }, + "deviceid": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "deviceid", + "openmrs_entity_id": "163149AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "subscriberid": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "subscriberid", + "openmrs_entity_id": "163150AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "simserial": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "simserial", + "openmrs_entity_id": "163151AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "phonenumber": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "phonenumber", + "openmrs_entity_id": "163152AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "encounter_location": "" + }, + "step1": { + "title": "{{step1.title}}", + "display_back_button": "true", + "previous_label": "{{step1.previous_label}}", + "bottom_navigation": "true", + "bottom_navigation_orientation": "vertical", + "next_type": "submit", + "submit_label": "{{step1.submit_label}}", + "next_form": "json.form/patient-registration-form.json", + "fields": [ + { + "key": "patient_name_label", + "type": "label", + "text": "{{step1.patient_name_label.text}}", + "text_color": "#000000" + }, + { + "key": "patient_name", + "openmrs_entity_parent": "", + "openmrs_entity": "person", + "openmrs_entity_id": "first_name", + "type": "edit_text", + "edit_type": "name" + }, + { + "key": "patient_id_label", + "type": "label", + "text": "Patient ID", + "text_color": "#000000" + }, + { + "key": "patient_id", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "edit_text", + "v_required": { + "value": "true", + "err": "Please enter patient ID" + } + }, + { + "key": "sex", + "openmrs_entity_parent": "", + "openmrs_entity": "person", + "openmrs_entity_id": "gender", + "type": "native_radio", + "label": "{{step1.sex.label}}", + "options": [ + { + "key": "Female", + "text": "{{step1.sex.options.Female.text}}" + }, + { + "key": "Male", + "text": "{{step1.sex.options.Male.text}}" + } + ], + "v_required": { + "value": "true", + "err": "{{step1.sex.v_required.err}}" + } + }, + { + "key": "patient_dob", + "openmrs_entity_parent": "", + "openmrs_entity": "person_attribute", + "openmrs_entity_id": "birthdate", + "type": "date_picker", + "hint": "{{step1.patient_dob.hint}}", + "expanded": false, + "duration": { + "label": "{{step1.patient_dob.duration.label}}" + }, + "min_date": "today-100y", + "max_date": "today" + }, + { + "key": "age", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "hidden", + "value": "" + }, + { + "key": "patient_occupation_label", + "type": "label", + "text": "{{step1.patient_occupation_label.text}}", + "text_color": "#000000" + }, + { + "key": "patient_occupation", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "edit_text", + "v_required": { + "value": "false", + "err": "" + } + }, + { + "key": "been_treated", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "native_radio", + "label": "{{step1.been_treated.label}}", + "options": [ + { + "key": "Yes", + "text": "{{step1.been_treated.options.Yes.text}}" + }, + { + "key": "No", + "text": "{{step1.been_treated.options.No.text}}" + }, + { + "key": "Not Answered", + "text": "{{step1.been_treated.options.not_answered.text}}" + } + ], + "v_required": { + "value": "true", + "err": "{{step1.been_treated.v_required.err}}" + } + } + ] + } +} \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/test_form_translation_interpolated_missing_translations b/android-json-form-wizard/src/test/resources/test_form_translation_interpolated_missing_translations new file mode 100644 index 000000000..85560e552 --- /dev/null +++ b/android-json-form-wizard/src/test/resources/test_form_translation_interpolated_missing_translations @@ -0,0 +1,179 @@ +{ + "count": "1", + "encounter_type": "patient_registration", + "entity_id": "", + "metadata": { + "start": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "start", + "openmrs_entity_id": "163137AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "end": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "end", + "openmrs_entity_id": "163138AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "today": { + "openmrs_entity_parent": "", + "openmrs_entity": "encounter", + "openmrs_entity_id": "encounter_date" + }, + "deviceid": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "deviceid", + "openmrs_entity_id": "163149AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "subscriberid": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "subscriberid", + "openmrs_entity_id": "163150AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "simserial": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "simserial", + "openmrs_entity_id": "163151AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "phonenumber": { + "openmrs_entity_parent": "", + "openmrs_entity": "concept", + "openmrs_data_type": "phonenumber", + "openmrs_entity_id": "163152AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "encounter_location": "" + }, + "step1": { + "title": "{{step1.title}}", + "display_back_button": "true", + "previous_label": "{{step1.previous_label}}", + "bottom_navigation": "true", + "bottom_navigation_orientation": "vertical", + "next_type": "submit", + "submit_label": "{{step1.submit_label}}", + "next_form": "json.form/patient-registration-form.json", + "fields": [ + { + "key": "patient_name_label", + "type": "label", + "text": "{{step1.patient_name_label.text}}", + "text_color": "#000000" + }, + { + "key": "patient_name", + "openmrs_entity_parent": "", + "openmrs_entity": "person", + "openmrs_entity_id": "first_name", + "type": "edit_text", + "edit_type": "name" + }, + { + "key": "patient_id_label", + "type": "label", + "text": "Patient ID", + "text_color": "#000000" + }, + { + "key": "patient_id", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "edit_text", + "v_required": { + "value": "true", + "err": "Please enter patient ID" + } + }, + { + "key": "sex", + "openmrs_entity_parent": "", + "openmrs_entity": "person", + "openmrs_entity_id": "gender", + "type": "native_radio", + "label": "{{step1.sex.label}}", + "options": [ + { + "key": "Female", + "text": "{{step1.sex.options.Female.text}}" + }, + { + "key": "Male", + "text": "{{step1.sex.options.Male.text}}" + } + ], + "v_required": { + "value": "true", + "err": "{{step1.sex.v_required.err}}" + } + }, + { + "key": "patient_dob", + "openmrs_entity_parent": "", + "openmrs_entity": "person_attribute", + "openmrs_entity_id": "birthdate", + "type": "date_picker", + "hint": "{{step1.patient_dob.hint}}", + "expanded": false, + "duration": { + "label": "{{step1.patient_dob.duration.label}}" + }, + "min_date": "today-100y", + "max_date": "today" + }, + { + "key": "age", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "hidden", + "value": "" + }, + { + "key": "patient_occupation_label", + "type": "label", + "text": "{{step1.patient_occupation_label.text}}", + "text_color": "#000000" + }, + { + "key": "patient_occupation", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "edit_text", + "v_required": { + "value": "false", + "err": "" + } + }, + { + "key": "been_treated", + "openmrs_entity_parent": "", + "openmrs_entity": "", + "openmrs_entity_id": "", + "type": "native_radio", + "label": "{{step1.been_treated.label}}", + "options": [ + { + "key": "Yes", + "text": "{{step1.been_treated.options.Yes.text}}" + }, + { + "key": "No", + "text": "{{step1.been_treated.options.No.text}}" + }, + { + "key": "Not Answered", + "text": "{{step1.been_treated.options.not_answered.text}}" + } + ], + "v_required": { + "value": "true", + "err": "{{step1.been_treated.v_required.err}}" + } + } + ] + } +} \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/test_yaml_translation_en_US b/android-json-form-wizard/src/test/resources/test_yaml_translation_en_US new file mode 100644 index 000000000..25b485c80 --- /dev/null +++ b/android-json-form-wizard/src/test/resources/test_yaml_translation_en_US @@ -0,0 +1,15 @@ +--- +properties_file_name: "yaml_strings", +sub_group: ultrasound_tests_results +fields: + - template: "Ultrasound test: {ultrasound}" + relevance: "ultrasound != ''" + + - template: "Ultrasound not done reason: {ultrasound_notdone}" + relevance: "ultrasound_notdone != ''" + + - template: "Ultrasound not done other reason: {ultrasound_notdone_other}" + relevance: "ultrasound_notdone_other != ''" + + - template: "Ultrasound done date: {ultrasound_date}" + relevance: "ultrasound_date != ''" \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/test_yaml_translation_interpolated b/android-json-form-wizard/src/test/resources/test_yaml_translation_interpolated new file mode 100644 index 000000000..87e84747d --- /dev/null +++ b/android-json-form-wizard/src/test/resources/test_yaml_translation_interpolated @@ -0,0 +1,15 @@ +--- +properties_file_name: "yaml_strings", +sub_group: ultrasound_tests_results +fields: + - template: "{{profile_contact_tab_tests.ultrasound_test}}: {ultrasound}" + relevance: "ultrasound != ''" + + - template: "{{profile_contact_tab_tests.ultrasound_test_not_done_reason}}: {ultrasound_notdone}" + relevance: "ultrasound_notdone != ''" + + - template: "{{profile_contact_tab_tests.ultrasound_test_not_done_other_reason}}: {ultrasound_notdone_other}" + relevance: "ultrasound_notdone_other != ''" + + - template: "{{profile_contact_tab_tests.ultrasound_test_done_date}}: {ultrasound_date}" + relevance: "ultrasound_date != ''" \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/test_yaml_translation_interpolated_missing_translations b/android-json-form-wizard/src/test/resources/test_yaml_translation_interpolated_missing_translations new file mode 100644 index 000000000..a48f7b469 --- /dev/null +++ b/android-json-form-wizard/src/test/resources/test_yaml_translation_interpolated_missing_translations @@ -0,0 +1,14 @@ +--- +sub_group: ultrasound_tests_results +fields: + - template: "{{profile_contact_tab_tests.ultrasound_test}}: {ultrasound}" + relevance: "ultrasound != ''" + + - template: "{{profile_contact_tab_tests.ultrasound_test_not_done_reason}}: {ultrasound_notdone}" + relevance: "ultrasound_notdone != ''" + + - template: "{{profile_contact_tab_tests.ultrasound_test_not_done_other_reason}}: {ultrasound_notdone_other}" + relevance: "ultrasound_notdone_other != ''" + + - template: "{{profile_contact_tab_tests.ultrasound_test_done_date}}: {ultrasound_date}" + relevance: "ultrasound_date != ''" \ No newline at end of file diff --git a/android-json-form-wizard/src/test/resources/yaml_strings_en_US.properties b/android-json-form-wizard/src/test/resources/yaml_strings_en_US.properties new file mode 100644 index 000000000..7d6edf73b --- /dev/null +++ b/android-json-form-wizard/src/test/resources/yaml_strings_en_US.properties @@ -0,0 +1,4 @@ +profile_contact_tab_tests.ultrasound_test=Ultrasound test +profile_contact_tab_tests.ultrasound_test_not_done_reason=Ultrasound not done reason +profile_contact_tab_tests.ultrasound_test_not_done_other_reason=Ultrasound not done other reason +profile_contact_tab_tests.ultrasound_test_done_date=Ultrasound done date diff --git a/gradle.properties b/gradle.properties index 3e858c2d3..a7799d056 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.7.24-SNAPSHOT +VERSION_NAME=1.7.25-SNAPSHOT VERSION_CODE=1 GROUP=org.smartregister POM_SETTING_DESCRIPTION=OpenSRP Client Native Form Json Wizard diff --git a/sample/src/main/java/org/smartregister/nativeform/MainActivity.java b/sample/src/main/java/org/smartregister/nativeform/MainActivity.java index bda98987b..3a14a2eae 100644 --- a/sample/src/main/java/org/smartregister/nativeform/MainActivity.java +++ b/sample/src/main/java/org/smartregister/nativeform/MainActivity.java @@ -65,19 +65,19 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } else if (id == R.id.action_single) { try { - startForm(REQUEST_CODE_GET_JSON, "single_form", null); + startForm(REQUEST_CODE_GET_JSON, "single_form", null, false); } catch (Exception e) { Log.e(TAG, e.getMessage(), e); } } else if (id == R.id.action_wizard) { try { - startForm(REQUEST_CODE_GET_JSON, "wizard_form", null); + startForm(REQUEST_CODE_GET_JSON, "wizard_form", null, false); } catch (Exception e) { Log.e(TAG, e.getMessage(), e); } } else if (id == R.id.action_validation) { try { - startForm(REQUEST_CODE_GET_JSON, "validation_form", null); + startForm(REQUEST_CODE_GET_JSON, "validation_form", null, false); } catch (Exception e) { Log.e(TAG, e.getMessage(), e); } @@ -97,8 +97,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } - public void startForm(int jsonFormActivityRequestCode, - String formName, String entityId) throws Exception { + public void startForm(int jsonFormActivityRequestCode, String formName, String entityId, boolean translate) throws Exception { final String STEP1 = "step1"; final String FIELDS = "fields"; @@ -211,6 +210,7 @@ public void startForm(int jsonFormActivityRequestCode, Intent intent = new Intent(this, JsonFormActivity.class); intent.putExtra("json", jsonForm.toString()); + intent.putExtra(JsonFormConstants.PERFORM_FORM_TRANSLATION, translate); Log.d(getClass().getName(), "form is " + jsonForm.toString()); startActivityForResult(intent, jsonFormActivityRequestCode); break; @@ -252,34 +252,34 @@ public void onClick(View view) { try { switch (id) { case R.id.child_enrollment: - startForm(REQUEST_CODE_GET_JSON, "single_form", null); + startForm(REQUEST_CODE_GET_JSON, "anc_register", null, true); break; case R.id.wizard_form: - startForm(REQUEST_CODE_GET_JSON, "wizard_form", null); + startForm(REQUEST_CODE_GET_JSON, "wizard_form", null, false); break; case R.id.native_form_basic: - startForm(REQUEST_CODE_GET_JSON, "basic_form", null); + startForm(REQUEST_CODE_GET_JSON, "basic_form", null, false); break; case R.id.rules_engine_skip_logic: - startForm(REQUEST_CODE_GET_JSON, "rules_engine_demo", null); + startForm(REQUEST_CODE_GET_JSON, "rules_engine_demo", null, false); break; case R.id.numbers_selector_widget: - startForm(REQUEST_CODE_GET_JSON, "constraints_demo", null); + startForm(REQUEST_CODE_GET_JSON, "constraints_demo", null, false); break; case R.id.generic_dialog_button: - startForm(REQUEST_CODE_GET_JSON, "generic_popup_form", null); + startForm(REQUEST_CODE_GET_JSON, "generic_popup_form", null, false); break; case R.id.validation_form_button: - startForm(REQUEST_CODE_GET_JSON, "validation_form", null); + startForm(REQUEST_CODE_GET_JSON, "validation_form", null, false); break; case R.id.expansion_panel_button: - startForm(REQUEST_CODE_GET_JSON, "expansion_panel_form", null); + startForm(REQUEST_CODE_GET_JSON, "expansion_panel_form", null, false); break; case R.id.repeating_group_button: - startForm(REQUEST_CODE_GET_JSON, "repeating_group", null); + startForm(REQUEST_CODE_GET_JSON, "repeating_group", null, false); break; case R.id.multiselect_list: - startForm(REQUEST_CODE_GET_JSON, "multi_select_list_form", null); + startForm(REQUEST_CODE_GET_JSON, "multi_select_list_form", null, false); break; default: break; diff --git a/sample/src/main/resources/anc_register_en_US.properties b/sample/src/main/resources/anc_register_en_US.properties new file mode 100644 index 000000000..8a06efeef --- /dev/null +++ b/sample/src/main/resources/anc_register_en_US.properties @@ -0,0 +1,35 @@ +anc_register.step1.title=ANC Registration +anc_register.step1.wom_image=Take a picture of the woman +anc_register.step1.anc_id.hint=ANC ID +anc_register.step1.anc_id.scanButtonText=Scan QR Code +anc_register.step1.anc_id.numeric.err=Please enter a valid ANC ID +anc_register.step1.anc_id.required.err=Please enter the Woman's ANC ID +anc_register.step1.first_name.hint=First name +anc_register.step1.first_name.required.err=Please enter the first name +anc_register.step1.first_name.regex.err=Please enter a valid name +anc_register.step1.last_name.hint=Last name +anc_register.step1.last_name.required.err=Please enter the last name +anc_register.step1.first_name.regex.err=Please enter a valid name +anc_register.step1.dob_entered.hint=Date of birth (DOB) +anc_register.step1.dob_entered.required.err=Please enter the date of birth +anc_register.step1.dob_unknown.option.text=DOB unknown? +anc_register.step1.age_entered.hint=Age +anc_register.step1.age_entered.constraint.err=Age must be a number +anc_register.step1.age_entered.constraint.err=Age must be equal to or greater than 10 +anc_register.step1.age_entered.constraint.err=Age must be equal or less than 49 +anc_register.step1.age_entered.required.err=Please enter the woman's age +anc_register.step1.home_address.hint=Home address +anc_register.step1.home_address.required.err=Please enter the woman's home address +anc_register.step1.phone_number.hint=Mobile phone number +anc_register.step1.age_entered.constraint.err=Phone number must be numeric +anc_register.step1.home_address.required.err=Please specify the woman's phone number +anc_register.step1.reminders.label=Woman wants to receive reminders during pregnancy +anc_register.step1.reminders.label_info_text=Does she want to receive reminders for care and messages regarding her health throughout her pregnancy? +anc_register.step1.reminders.option.text=Yes +anc_register.step1.reminders.option.text=No +anc_register.step1.reminders.required.err=Please select whether the woman has agreed to receiving reminder notifications +anc_register.step1.alt_name.hint=Alternate contact name +anc_register.step1.alt_name.regex.err=Please enter a valid name +anc_register.step1.alt_phone_number.hint=Alternate contact phone number +anc_register.step1.alt_name.regex.err=Phone number must be numeric + diff --git a/sample/src/main/resources/anc_register_fr.properties b/sample/src/main/resources/anc_register_fr.properties new file mode 100644 index 000000000..6ab630b67 --- /dev/null +++ b/sample/src/main/resources/anc_register_fr.properties @@ -0,0 +1,35 @@ +anc_register.step1.title=Enregistrement CPN +anc_register.step1.wom_image=Prenez une photo de la femme +anc_register.step1.anc_id.hint=ID CPN +anc_register.step1.anc_id.scanButtonText=Scanner le code QR +anc_register.step1.anc_id.numeric.err=Entrez un ID CPN valide SVP +anc_register.step1.anc_id.required.err=Entrez un ID CPN SVP +anc_register.step1.first_name.hint=Prénom +anc_register.step1.first_name.required.err=Entrez le prénom SVP +anc_register.step1.first_name.regex.err=Entrez un prénom valide SVP +anc_register.step1.last_name.hint=Nom +anc_register.step1.last_name.required.err=Entrez le nom SVP +anc_register.step1.first_name.regex.err=Entrez un nom valide SVP +anc_register.step1.dob_entered.hint=Date de naissance (DDN) +anc_register.step1.dob_entered.required.err=Entrez la DDN SVP +anc_register.step1.dob_unknown.option.text=DDN inconnue? +anc_register.step1.age_entered.hint=Âge +anc_register.step1.age_entered.constraint.err=L'âge doit être un nombre +anc_register.step1.age_entered.constraint.err=L'âge doit être supérieur ou égal à 10 +anc_register.step1.age_entered.constraint.err=L'âge doit être égal ou inférieur à 49 +anc_register.step1.age_entered.required.err=Veuillez entrer l'âge de la femme +anc_register.step1.home_address.hint=Adresse du domicile +anc_register.step1.home_address.required.err=Veuillez entrer l'adresse du domicile de la femme +anc_register.step1.phone_number.hint=Numéro de téléphone +anc_register.step1.age_entered.constraint.err=Le numéro de téléphone doit être numérique +anc_register.step1.home_address.required.err=Veuillez préciser le numéro de téléphone de la femme +anc_register.step1.reminders.label=La femme veut recevoir des rappels pendant la grossesse +anc_register.step1.reminders.label_info_text=Veut-elle recevoir des rappels de soins et des messages concernant sa santé tout au long de sa grossesse? +anc_register.step1.reminders.option.text=Oui +anc_register.step1.reminders.option.text=Non +anc_register.step1.reminders.required.err=Veuillez indiquer si la femme a accepté de recevoir des notifications de rappel +anc_register.step1.alt_name.hint=Nom de contact alternatif +anc_register.step1.alt_name.regex.err=Entrer un nom valide +anc_register.step1.alt_phone_number.hint=Autre numéro de téléphone de contact +anc_register.step1.alt_name.regex.err=Le numéro de téléphone doit être numérique +