mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Config flow translations (#13066)
* Development script for testing translation strings * Localize backend of config flow * Fix hue tests * Update hue.en.json * Move components to individual directories * Bridge -> bridge
This commit is contained in:
parent
f5cc40024d
commit
26960283a0
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"error": {
|
||||||
|
"invalid_object_id": "Invalid object ID"
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"object_id": "Object ID"
|
||||||
|
},
|
||||||
|
"description": "Please enter an object_id for the test entity.",
|
||||||
|
"title": "Pick object id"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"data": {
|
||||||
|
"name": "Name"
|
||||||
|
},
|
||||||
|
"description": "Please enter a name for the test entity.",
|
||||||
|
"title": "Name of the entity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Config Entry Example"
|
||||||
|
}
|
||||||
|
}
|
@ -62,13 +62,11 @@ class ExampleConfigFlow(config_entries.ConfigFlowHandler):
|
|||||||
return (yield from self.async_step_name())
|
return (yield from self.async_step_name())
|
||||||
|
|
||||||
errors = {
|
errors = {
|
||||||
'object_id': 'Invalid object id.'
|
'object_id': 'invalid_object_id'
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title='Pick object id',
|
|
||||||
step_id='init',
|
step_id='init',
|
||||||
description="Please enter an object_id for the test entity.",
|
|
||||||
data_schema=vol.Schema({
|
data_schema=vol.Schema({
|
||||||
'object_id': str
|
'object_id': str
|
||||||
}),
|
}),
|
||||||
@ -92,9 +90,7 @@ class ExampleConfigFlow(config_entries.ConfigFlowHandler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title='Name of the entity',
|
|
||||||
step_id='name',
|
step_id='name',
|
||||||
description="Please enter a name for the test entity.",
|
|
||||||
data_schema=vol.Schema({
|
data_schema=vol.Schema({
|
||||||
'name': str
|
'name': str
|
||||||
}),
|
}),
|
24
homeassistant/components/config_entry_example/strings.json
Normal file
24
homeassistant/components/config_entry_example/strings.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"title": "Config Entry Example",
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"title": "Pick object id",
|
||||||
|
"description": "Please enter an object_id for the test entity.",
|
||||||
|
"data": {
|
||||||
|
"object_id": "Object ID"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"title": "Name of the entity",
|
||||||
|
"description": "Please enter a name for the test entity.",
|
||||||
|
"data": {
|
||||||
|
"name": "Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_object_id": "Invalid object ID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
homeassistant/components/hue/.translations/en.json
Normal file
26
homeassistant/components/hue/.translations/en.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"all_configured": "All Philips Hue bridges are already configured",
|
||||||
|
"discover_timeout": "Unable to discover Hue bridges",
|
||||||
|
"no_bridges": "No Philips Hue bridges discovered"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"linking": "Unknown linking error occurred.",
|
||||||
|
"register_failed": "Failed to register, please try again"
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"host": "Host"
|
||||||
|
},
|
||||||
|
"title": "Pick Hue bridge"
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"description": "Press the button on the bridge to register Philips Hue with Home Assistant.\n\n",
|
||||||
|
"title": "Link Hub"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Philips Hue Bridge"
|
||||||
|
}
|
||||||
|
}
|
@ -304,12 +304,12 @@ class HueFlowHandler(config_entries.ConfigFlowHandler):
|
|||||||
bridges = await discover_nupnp(websession=self._websession)
|
bridges = await discover_nupnp(websession=self._websession)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
return self.async_abort(
|
return self.async_abort(
|
||||||
reason='Unable to discover Hue bridges.'
|
reason='discover_timeout'
|
||||||
)
|
)
|
||||||
|
|
||||||
if not bridges:
|
if not bridges:
|
||||||
return self.async_abort(
|
return self.async_abort(
|
||||||
reason='No Philips Hue bridges discovered.'
|
reason='no_bridges'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find already configured hosts
|
# Find already configured hosts
|
||||||
@ -322,7 +322,7 @@ class HueFlowHandler(config_entries.ConfigFlowHandler):
|
|||||||
|
|
||||||
if not hosts:
|
if not hosts:
|
||||||
return self.async_abort(
|
return self.async_abort(
|
||||||
reason='All Philips Hue bridges are already configured.'
|
reason='all_configured'
|
||||||
)
|
)
|
||||||
|
|
||||||
elif len(hosts) == 1:
|
elif len(hosts) == 1:
|
||||||
@ -331,7 +331,6 @@ class HueFlowHandler(config_entries.ConfigFlowHandler):
|
|||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id='init',
|
step_id='init',
|
||||||
title='Pick Hue Bridge',
|
|
||||||
data_schema=vol.Schema({
|
data_schema=vol.Schema({
|
||||||
vol.Required('host'): vol.In(hosts)
|
vol.Required('host'): vol.In(hosts)
|
||||||
})
|
})
|
||||||
@ -352,10 +351,10 @@ class HueFlowHandler(config_entries.ConfigFlowHandler):
|
|||||||
await bridge.initialize()
|
await bridge.initialize()
|
||||||
except (asyncio.TimeoutError, aiohue.RequestError,
|
except (asyncio.TimeoutError, aiohue.RequestError,
|
||||||
aiohue.LinkButtonNotPressed):
|
aiohue.LinkButtonNotPressed):
|
||||||
errors['base'] = 'Failed to register, please try again.'
|
errors['base'] = 'register_failed'
|
||||||
except aiohue.AiohueException:
|
except aiohue.AiohueException:
|
||||||
errors['base'] = 'Unknown linking error occurred.'
|
errors['base'] = 'linking'
|
||||||
_LOGGER.exception('Uknown Hue linking error occurred')
|
_LOGGER.exception('Unknown Hue linking error occurred')
|
||||||
else:
|
else:
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=bridge.config.name,
|
title=bridge.config.name,
|
||||||
@ -368,8 +367,6 @@ class HueFlowHandler(config_entries.ConfigFlowHandler):
|
|||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id='link',
|
step_id='link',
|
||||||
title='Link Hub',
|
|
||||||
description=CONFIG_INSTRUCTIONS,
|
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
26
homeassistant/components/hue/strings.json
Normal file
26
homeassistant/components/hue/strings.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"title": "Philips Hue Bridge",
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"title": "Pick Hue bridge",
|
||||||
|
"data": {
|
||||||
|
"host": "Host"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"title": "Link Hub",
|
||||||
|
"description": "Press the button on the bridge to register Philips Hue with Home Assistant.\n\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"register_failed": "Failed to register, please try again",
|
||||||
|
"linking": "Unknown linking error occurred."
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"discover_timeout": "Unable to discover Hue bridges",
|
||||||
|
"no_bridges": "No Philips Hue bridges discovered",
|
||||||
|
"all_configured": "All Philips Hue bridges are already configured"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -442,7 +442,7 @@ class FlowManager:
|
|||||||
'Handler returned incorrect type: {}'.format(result['type']))
|
'Handler returned incorrect type: {}'.format(result['type']))
|
||||||
|
|
||||||
if result['type'] == RESULT_TYPE_FORM:
|
if result['type'] == RESULT_TYPE_FORM:
|
||||||
flow.cur_step = (result.pop('step_id'), result['data_schema'])
|
flow.cur_step = (result['step_id'], result['data_schema'])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Abort and Success results both finish the flow
|
# Abort and Success results both finish the flow
|
||||||
@ -468,6 +468,7 @@ class ConfigFlowHandler:
|
|||||||
# Set by flow manager
|
# Set by flow manager
|
||||||
flow_id = None
|
flow_id = None
|
||||||
hass = None
|
hass = None
|
||||||
|
domain = None
|
||||||
source = SOURCE_USER
|
source = SOURCE_USER
|
||||||
cur_step = None
|
cur_step = None
|
||||||
|
|
||||||
@ -475,15 +476,13 @@ class ConfigFlowHandler:
|
|||||||
# VERSION
|
# VERSION
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_show_form(self, *, title, step_id, description=None,
|
def async_show_form(self, *, step_id, data_schema=None, errors=None):
|
||||||
data_schema=None, errors=None):
|
|
||||||
"""Return the definition of a form to gather user input."""
|
"""Return the definition of a form to gather user input."""
|
||||||
return {
|
return {
|
||||||
'type': RESULT_TYPE_FORM,
|
'type': RESULT_TYPE_FORM,
|
||||||
'flow_id': self.flow_id,
|
'flow_id': self.flow_id,
|
||||||
'title': title,
|
'domain': self.domain,
|
||||||
'step_id': step_id,
|
'step_id': step_id,
|
||||||
'description': description,
|
|
||||||
'data_schema': data_schema,
|
'data_schema': data_schema,
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
}
|
}
|
||||||
@ -494,6 +493,7 @@ class ConfigFlowHandler:
|
|||||||
return {
|
return {
|
||||||
'type': RESULT_TYPE_CREATE_ENTRY,
|
'type': RESULT_TYPE_CREATE_ENTRY,
|
||||||
'flow_id': self.flow_id,
|
'flow_id': self.flow_id,
|
||||||
|
'domain': self.domain,
|
||||||
'title': title,
|
'title': title,
|
||||||
'data': data,
|
'data': data,
|
||||||
}
|
}
|
||||||
@ -504,5 +504,6 @@ class ConfigFlowHandler:
|
|||||||
return {
|
return {
|
||||||
'type': RESULT_TYPE_ABORT,
|
'type': RESULT_TYPE_ABORT,
|
||||||
'flow_id': self.flow_id,
|
'flow_id': self.flow_id,
|
||||||
|
'domain': self.domain,
|
||||||
'reason': reason
|
'reason': reason
|
||||||
}
|
}
|
||||||
|
21
script/translations_develop
Executable file
21
script/translations_develop
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Compile the current translation strings files for testing
|
||||||
|
|
||||||
|
# Safe bash settings
|
||||||
|
# -e Exit on command fail
|
||||||
|
# -u Exit on unset variable
|
||||||
|
# -o pipefail Exit if piped command has error code
|
||||||
|
set -eu -o pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
mkdir -p build/translations-download
|
||||||
|
|
||||||
|
script/translations_upload_merge.py
|
||||||
|
|
||||||
|
# Use the generated translations upload file as the mock output from the
|
||||||
|
# Lokalise download
|
||||||
|
mv build/translations-upload.json build/translations-download/en.json
|
||||||
|
|
||||||
|
script/translations_download_split.py
|
@ -101,9 +101,7 @@ def test_initialize_flow(hass, client):
|
|||||||
schema[vol.Required('password')] = str
|
schema[vol.Required('password')] = str
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title='test-title',
|
|
||||||
step_id='init',
|
step_id='init',
|
||||||
description='test-description',
|
|
||||||
data_schema=schema,
|
data_schema=schema,
|
||||||
errors={
|
errors={
|
||||||
'username': 'Should be unique.'
|
'username': 'Should be unique.'
|
||||||
@ -121,8 +119,8 @@ def test_initialize_flow(hass, client):
|
|||||||
|
|
||||||
assert data == {
|
assert data == {
|
||||||
'type': 'form',
|
'type': 'form',
|
||||||
'title': 'test-title',
|
'domain': 'test',
|
||||||
'description': 'test-description',
|
'step_id': 'init',
|
||||||
'data_schema': [
|
'data_schema': [
|
||||||
{
|
{
|
||||||
'name': 'username',
|
'name': 'username',
|
||||||
@ -157,6 +155,7 @@ def test_abort(hass, client):
|
|||||||
data = yield from resp.json()
|
data = yield from resp.json()
|
||||||
data.pop('flow_id')
|
data.pop('flow_id')
|
||||||
assert data == {
|
assert data == {
|
||||||
|
'domain': 'test',
|
||||||
'reason': 'bla',
|
'reason': 'bla',
|
||||||
'type': 'abort'
|
'type': 'abort'
|
||||||
}
|
}
|
||||||
@ -186,6 +185,7 @@ def test_create_account(hass, client):
|
|||||||
data = yield from resp.json()
|
data = yield from resp.json()
|
||||||
data.pop('flow_id')
|
data.pop('flow_id')
|
||||||
assert data == {
|
assert data == {
|
||||||
|
'domain': 'test',
|
||||||
'title': 'Test Entry',
|
'title': 'Test Entry',
|
||||||
'type': 'create_entry'
|
'type': 'create_entry'
|
||||||
}
|
}
|
||||||
@ -203,7 +203,6 @@ def test_two_step_flow(hass, client):
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_step_init(self, user_input=None):
|
def async_step_init(self, user_input=None):
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title='test-title',
|
|
||||||
step_id='account',
|
step_id='account',
|
||||||
data_schema=vol.Schema({
|
data_schema=vol.Schema({
|
||||||
'user_title': str
|
'user_title': str
|
||||||
@ -224,8 +223,8 @@ def test_two_step_flow(hass, client):
|
|||||||
flow_id = data.pop('flow_id')
|
flow_id = data.pop('flow_id')
|
||||||
assert data == {
|
assert data == {
|
||||||
'type': 'form',
|
'type': 'form',
|
||||||
'title': 'test-title',
|
'domain': 'test',
|
||||||
'description': None,
|
'step_id': 'account',
|
||||||
'data_schema': [
|
'data_schema': [
|
||||||
{
|
{
|
||||||
'name': 'user_title',
|
'name': 'user_title',
|
||||||
@ -243,6 +242,7 @@ def test_two_step_flow(hass, client):
|
|||||||
data = yield from resp.json()
|
data = yield from resp.json()
|
||||||
data.pop('flow_id')
|
data.pop('flow_id')
|
||||||
assert data == {
|
assert data == {
|
||||||
|
'domain': 'test',
|
||||||
'type': 'create_entry',
|
'type': 'create_entry',
|
||||||
'title': 'user-title',
|
'title': 'user-title',
|
||||||
}
|
}
|
||||||
@ -262,7 +262,6 @@ def test_get_progress_index(hass, client):
|
|||||||
def async_step_account(self, user_input=None):
|
def async_step_account(self, user_input=None):
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id='account',
|
step_id='account',
|
||||||
title='Finish setup'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch.dict(HANDLERS, {'test': TestFlow}):
|
with patch.dict(HANDLERS, {'test': TestFlow}):
|
||||||
@ -292,9 +291,7 @@ def test_get_progress_flow(hass, client):
|
|||||||
schema[vol.Required('password')] = str
|
schema[vol.Required('password')] = str
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title='test-title',
|
|
||||||
step_id='init',
|
step_id='init',
|
||||||
description='test-description',
|
|
||||||
data_schema=schema,
|
data_schema=schema,
|
||||||
errors={
|
errors={
|
||||||
'username': 'Should be unique.'
|
'username': 'Should be unique.'
|
||||||
|
@ -552,7 +552,7 @@ async def test_flow_link_timeout(hass):
|
|||||||
assert result['type'] == 'form'
|
assert result['type'] == 'form'
|
||||||
assert result['step_id'] == 'link'
|
assert result['step_id'] == 'link'
|
||||||
assert result['errors'] == {
|
assert result['errors'] == {
|
||||||
'base': 'Failed to register, please try again.'
|
'base': 'register_failed'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -568,7 +568,7 @@ async def test_flow_link_button_not_pressed(hass):
|
|||||||
assert result['type'] == 'form'
|
assert result['type'] == 'form'
|
||||||
assert result['step_id'] == 'link'
|
assert result['step_id'] == 'link'
|
||||||
assert result['errors'] == {
|
assert result['errors'] == {
|
||||||
'base': 'Failed to register, please try again.'
|
'base': 'register_failed'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -584,5 +584,5 @@ async def test_flow_link_unknown_host(hass):
|
|||||||
assert result['type'] == 'form'
|
assert result['type'] == 'form'
|
||||||
assert result['step_id'] == 'link'
|
assert result['step_id'] == 'link'
|
||||||
assert result['errors'] == {
|
assert result['errors'] == {
|
||||||
'base': 'Failed to register, please try again.'
|
'base': 'register_failed'
|
||||||
}
|
}
|
||||||
|
@ -226,14 +226,14 @@ def test_configure_reuses_handler_instance(manager):
|
|||||||
def async_step_init(self, user_input=None):
|
def async_step_init(self, user_input=None):
|
||||||
self.handle_count += 1
|
self.handle_count += 1
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title=str(self.handle_count),
|
errors={'base': str(self.handle_count)},
|
||||||
step_id='init')
|
step_id='init')
|
||||||
|
|
||||||
with patch.dict(config_entries.HANDLERS, {'test': TestFlow}):
|
with patch.dict(config_entries.HANDLERS, {'test': TestFlow}):
|
||||||
form = yield from manager.flow.async_init('test')
|
form = yield from manager.flow.async_init('test')
|
||||||
assert form['title'] == '1'
|
assert form['errors']['base'] == '1'
|
||||||
form = yield from manager.flow.async_configure(form['flow_id'])
|
form = yield from manager.flow.async_configure(form['flow_id'])
|
||||||
assert form['title'] == '2'
|
assert form['errors']['base'] == '2'
|
||||||
assert len(manager.flow.async_progress()) == 1
|
assert len(manager.flow.async_progress()) == 1
|
||||||
assert len(manager.async_entries()) == 0
|
assert len(manager.async_entries()) == 0
|
||||||
|
|
||||||
@ -250,7 +250,6 @@ def test_configure_two_steps(manager):
|
|||||||
self.init_data = user_input
|
self.init_data = user_input
|
||||||
return self.async_step_second()
|
return self.async_step_second()
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title='title',
|
|
||||||
step_id='init',
|
step_id='init',
|
||||||
data_schema=vol.Schema([str])
|
data_schema=vol.Schema([str])
|
||||||
)
|
)
|
||||||
@ -263,7 +262,6 @@ def test_configure_two_steps(manager):
|
|||||||
data=self.init_data + user_input
|
data=self.init_data + user_input
|
||||||
)
|
)
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title='title',
|
|
||||||
step_id='second',
|
step_id='second',
|
||||||
data_schema=vol.Schema([str])
|
data_schema=vol.Schema([str])
|
||||||
)
|
)
|
||||||
@ -299,9 +297,7 @@ def test_show_form(manager):
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_step_init(self, user_input=None):
|
def async_step_init(self, user_input=None):
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
title='Hello form',
|
|
||||||
step_id='init',
|
step_id='init',
|
||||||
description='test-description',
|
|
||||||
data_schema=schema,
|
data_schema=schema,
|
||||||
errors={
|
errors={
|
||||||
'username': 'Should be unique.'
|
'username': 'Should be unique.'
|
||||||
@ -311,8 +307,6 @@ def test_show_form(manager):
|
|||||||
with patch.dict(config_entries.HANDLERS, {'test': TestFlow}):
|
with patch.dict(config_entries.HANDLERS, {'test': TestFlow}):
|
||||||
form = yield from manager.flow.async_init('test')
|
form = yield from manager.flow.async_init('test')
|
||||||
assert form['type'] == 'form'
|
assert form['type'] == 'form'
|
||||||
assert form['title'] == 'Hello form'
|
|
||||||
assert form['description'] == 'test-description'
|
|
||||||
assert form['data_schema'] is schema
|
assert form['data_schema'] is schema
|
||||||
assert form['errors'] == {
|
assert form['errors'] == {
|
||||||
'username': 'Should be unique.'
|
'username': 'Should be unique.'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user