Skip to content

Commit c1caee6

Browse files
Override languages names (#2304)
* Refactor language handling Unify iso639 handling in a single utility module so it's easier to change its behaviour in the app * Expose custom language names from the app If they are enabled via an environment variable, the list of language names overrides will be exposed to the UI as part of the appConfig object. * Override language names in UI when enabled * Disable flow typing in language.js Couldn't get it to type the `filter` expression in `livingLanguages`, and we're set to remove Flow altogether soon anyways. * Fix linter issue Code is basically the same, but eslint likes this one better * Move custom languages to an environment variable So we can customise this per-instance instead of hard-coding a single list of mappings
1 parent 1d24132 commit c1caee6

File tree

10 files changed

+45
-31
lines changed

10 files changed

+45
-31
lines changed

assets/js/components/activity/ActivityDescription.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { Component, PropTypes } from "react"
22
import { translate } from "react-i18next"
3-
import { translateLangCode } from "../timezones/util"
3+
import { codeToName } from "../../language"
44

55
class ActivityDescription extends Component {
66
reportTypeText(reportType) {
@@ -216,12 +216,12 @@ class ActivityDescription extends Component {
216216
case "add_language":
217217
return t("Added language <i>{{language}}</i> to <i>{{questionnaireName}}", {
218218
questionnaireName,
219-
language: translateLangCode(metadata["language"]),
219+
language: codeToName(metadata["language"]),
220220
})
221221
case "remove_language":
222222
return t("Removed language <i>{{language}}</i> from <i>{{questionnaireName}}", {
223223
questionnaireName,
224-
language: translateLangCode(metadata["language"]),
224+
language: codeToName(metadata["language"]),
225225
})
226226
case "create_step":
227227
return t("Added step <i>{{stepTitle}}</i> to <i>{{questionnaireName}}</i>", {

assets/js/components/questionnaires/AddLanguage.jsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as actions from "../../actions/questionnaire"
22
import React, { Component, PropTypes } from "react"
33
import { Autocomplete } from "../ui"
4-
import iso6393 from "iso-639-3"
4+
import { idForLanguage, livingLanguages } from "../../language"
55
import "materialize-autocomplete"
66
import withQuestionnaire from "./withQuestionnaire"
77
import { translate } from "react-i18next"
@@ -28,13 +28,11 @@ class AddLanguage extends Component {
2828

2929
autocompleteGetData(value, callback) {
3030
const { questionnaire } = this.props
31-
const idForLanguange = (lang) => (lang.iso6391 ? lang.iso6391 : lang.iso6393)
3231
const matchesLanguage = (value, lang) =>
3332
lang.text.indexOf(value) !== -1 || lang.text.toLowerCase().indexOf(value) !== -1
3433

35-
let languagesOptions = iso6393
36-
.filter((lang) => lang.type == "living")
37-
.map((lang) => ({ id: idForLanguange(lang), text: lang.name }))
34+
let languagesOptions = livingLanguages()
35+
.map((lang) => ({ id: idForLanguage(lang), text: lang.name }))
3836

3937
// Don't show languages that are already selected
4038
languagesOptions = languagesOptions.filter(

assets/js/components/questionnaires/LanguagesList.jsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as actions from "../../actions/questionnaire"
33
import React, { Component } from "react"
44
import { connect } from "react-redux"
55
import "materialize-autocomplete"
6-
import { translateLangCode } from "../timezones/util"
6+
import { codeToName } from "../../language"
77
import classNames from "classnames/bind"
88
import AddLanguage from "./AddLanguage"
99
import { hasErrorsInLanguage } from "../../questionnaireErrors"
@@ -103,8 +103,8 @@ class LanguagesList extends Component<Props> {
103103

104104
let otherLanguages = languages
105105
.filter((lang) => lang !== defaultLanguage)
106-
.map((lang: string) => [lang, translateLangCode(lang)])
107-
.sort(([c1, n1], [c2, n2]) => n1.localeCompare(n2))
106+
.map((lang: string) => [lang, codeToName(lang)])
107+
.sort(([c1, n1], [c2, n2]) => (n1 || '').localeCompare((n2 || '')))
108108
.map(([code, name]) => this.renderLanguageRow(code, name))
109109

110110
let otherLanguagesComponent = null
@@ -117,7 +117,7 @@ class LanguagesList extends Component<Props> {
117117
return (
118118
<div className="languages">
119119
<LanguageSection title={t("Primary language")}>
120-
{this.renderLanguageRow(defaultLanguage, translateLangCode(defaultLanguage), true)}
120+
{this.renderLanguageRow(defaultLanguage, codeToName(defaultLanguage), true)}
121121
</LanguageSection>
122122
{otherLanguagesComponent}
123123
{readOnly ? null : (

assets/js/components/questionnaires/StepLanguageSelection.jsx

+2-7
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@ import React, { Component, PropTypes } from "react"
22
import { connect } from "react-redux"
33
import * as actions from "../../actions/questionnaire"
44
import { Input } from "react-materialize"
5-
import iso6393 from "iso-639-3"
5+
import { codeToName } from "../../language"
66
import { Card } from "../ui"
77
import { translate } from "react-i18next"
88

99
class StepLanguageSelection extends Component {
10-
translateLangCode(code) {
11-
const language = iso6393.find((lang) => lang.iso6391 == code || lang.iso6393 == code)
12-
return language.name
13-
}
14-
1510
changeLanguageOrder(choice, event) {
1611
const { dispatch } = this.props
1712
event.preventDefault()
@@ -46,7 +41,7 @@ class StepLanguageSelection extends Component {
4641
<tbody>
4742
{languageChoices.map((choice, index) => (
4843
<tr key={`${choice}${index}`}>
49-
<td>{this.translateLangCode(choice)}</td>
44+
<td>{codeToName(choice)}</td>
5045
<td>
5146
<Input
5247
s={8}

assets/js/components/questionnaires/StepNumericEditor.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { bindActionCreators } from "redux"
66
import * as questionnaireActions from "../../actions/questionnaire"
77
import { newRefusal } from "../../step"
88
import ChoiceEditor from "./ChoiceEditor"
9-
import { translateLangCode } from "../timezones/util"
9+
import { codeToName } from "../../language"
1010
import SkipLogic from "./SkipLogic"
1111
import propsAreEqual from "../../propsAreEqual"
1212
import { config } from "../../config"
@@ -357,7 +357,7 @@ class StepNumericEditor extends Component<any, State> {
357357
questionnaire.languages,
358358
config.available_languages_for_numbers
359359
)
360-
.map((lang) => translateLangCode(lang))
360+
.map((lang) => codeToName(lang))
361361
.join(", ")
362362

363363
alphabeticalAnswersComponent = (
-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import iso6393 from "iso-639-3"
21
export const formatTimezone = (tz) => {
32
const split = (tz || "UTC").replace("_", " ").split("/")
43
switch (split.length) {
@@ -10,8 +9,3 @@ export const formatTimezone = (tz) => {
109
return split[0]
1110
}
1211
}
13-
14-
export const translateLangCode = (code) => {
15-
const language = iso6393.find((lang) => lang.iso6391 == code || lang.iso6393 == code)
16-
return language.name
17-
}

assets/js/config.js

+2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ type Config = {
1010
nuntium: ProviderConfig[],
1111
verboice: ProviderConfig[],
1212
available_languages_for_numbers: string[],
13+
custom_language_names: {[string]: string},
1314
}
1415

1516
const defaultConfig = {
1617
available_languages_for_numbers: ["en"],
18+
custom_language_names: {},
1719
user_settings: {
1820
language: "en",
1921
},

assets/js/language.js

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
1-
// @flow
21
import iso6393 from "iso-639-3"
2+
import { config } from "./config"
3+
4+
const languages = (() => {
5+
let langs = iso6393
6+
for (const [code, name] of Object.entries(config.custom_language_names)) {
7+
let lang = langs.find((l) => l.iso6393 == code)
8+
if (lang) {
9+
lang.name = name
10+
}
11+
}
12+
return langs
13+
})()
314

415
export function nameToCode(name: string): ?string {
5-
let match = iso6393.find((l) => l.name == name)
6-
return match ? match.iso6391 || match.iso6393 : null
16+
let match = languages.find((l) => l.name == name)
17+
return match ? idForLanguage(match) : null
18+
}
19+
20+
export const idForLanguage = (lang): string => (lang.iso6391 ? lang.iso6391 : lang.iso6393)
21+
22+
export function livingLanguages() {
23+
return languages.filter((lang) => lang.type == "living")
724
}
825

926
export function codeToName(code: string): ?string {
10-
let match = iso6393.find((l) => l.iso6391 == code || l.iso6393 == code)
27+
let match = languages.find((l) => l.iso6391 == code || l.iso6393 == code)
1128
return match ? match.name : null
1229
}

config/config.exs

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ config :ask, Ask.Runtime.QuestionnaireSimulatorStore,
5858
config :ask, AskWeb.Email,
5959
smtp_from_address: {:system, "SMTP_FROM_ADDRESS", "InSTEDD Surveda <noreply@instedd.org>"}
6060

61+
config :ask, custom_language_names: System.get_env("CUSTOM_LANGUAGE_NAMES")
62+
6163
# Configures Elixir's Logger
6264
config :logger, :console,
6365
format: "$dateT$timeZ $metadata[$level] $message\n",

lib/ask_web/views/layout_view.ex

+6
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@ defmodule AskWeb.LayoutView do
2828
end
2929
end
3030

31+
custom_language_names = case Poison.decode(Application.get_env(:ask, :custom_language_names) || "") do
32+
{:ok, custom_languages} -> custom_languages
33+
_ -> %{}
34+
end
35+
3136
client_config = %{
3237
version: version,
3338
csrf_token: get_csrf_token(),
3439
user: user_email,
3540
user_settings: user_settings,
3641
sentryDsn: sentry_dsn,
3742
available_languages_for_numbers: Ask.NumberTranslator.langs(),
43+
custom_language_names: custom_language_names,
3844
nuntium: Config.provider_config(Nuntium) |> guisso_configs,
3945
verboice: Config.provider_config(Verboice) |> guisso_configs,
4046
intercom_app_id: Ask.Intercom.intercom_app_id()

0 commit comments

Comments
 (0)