diff --git a/src/server_manager/bower.json b/src/server_manager/bower.json index d2598de2..6717a6a1 100644 --- a/src/server_manager/bower.json +++ b/src/server_manager/bower.json @@ -16,12 +16,14 @@ ], "dependencies": { "app-layout": "PolymerElements/app-layout#^2.0.4", + "app-localize-behavior": "PolymerElements/app-localize-behavior#^2.0.1", "font-roboto": "PolymerElements/font-roboto-local#^1.1.0", "iron-autogrow-textarea": "PolymerElements/iron-autogrow-textarea#^2.1.1", "iron-icons": "PolymerElements/iron-icons#^2.0.1", "iron-collapse": "PolymerElements/iron-collapse#^2.2.1", "iron-fit-behavior": "PolymerElements/iron-fit-behavior#^2.1.0", "iron-pages": "PolymerElements/iron-pages#^2.0.1", + "outline-i18n": "Jigsaw-Code/outline-i18n#^0.0.7", "paper-button": "PolymerElements/paper-button#^2.0.0", "paper-checkbox": "PolymerElements/paper-checkbox#^2.0.4", "paper-dialog": "PolymerElements/paper-dialog#^2.0.1", diff --git a/src/server_manager/index.html b/src/server_manager/index.html index 892173f9..27011b2e 100644 --- a/src/server_manager/index.html +++ b/src/server_manager/index.html @@ -18,7 +18,6 @@ - @@ -26,6 +25,7 @@ + @@ -47,7 +47,7 @@ Outline Manager - + diff --git a/src/server_manager/messages/en.json b/src/server_manager/messages/en.json new file mode 100644 index 00000000..412ced3e --- /dev/null +++ b/src/server_manager/messages/en.json @@ -0,0 +1,196 @@ +{ + "about-outline": "Outline is an open source project created by Jigsaw to provide a safer way for news organizations and journalists to access the internet.

Outline is powered by Shadowsocks and is still an early stage product. You can contribute to the code on GitHub, and follow us on Reddit and Medium to hear when we expand to more platforms and add new features.", + "about-version": "Version {version}", + "aws-group-assign-0": "Navigate to {openLink}Instances{closeLink}.", + "aws-group-assign-1": "Click 'Actions' > 'Networking' > 'Change Security Groups' at the top of the 'Instances' screen.", + "aws-group-assign-2": "Select the 'outline' Security Group, unselect other security groups.", + "aws-group-assign-3": "Click 'Assign Security Groups'.", + "aws-group-create-0": "Once you've created your EC2 instance, open the {openLink}EC2 Management Console{closeLink}.", + "aws-group-create-1": "Under 'Network & Security', select 'Security Groups'.", + "aws-group-create-2": "Click on 'Create Security Group'.", + "aws-group-create-3": "Type 'outline' in the 'Security group name' and 'Description' fields.", + "aws-group-create-4": "Click 'Add Rule'.", + "aws-group-create-5": "Select 'All traffic' under 'Type', and 'Anywhere' under 'Source'.", + "cancel": "Cancel", + "city-ams": "Amsterdam", + "city-blr": "Bangalore", + "city-fra": "Frankfurt", + "city-lon": "London", + "city-nyc": "New York", + "city-sfo": "San Francisco", + "city-sgp": "Singapore", + "city-tor": "Toronto", + "close": "Close", + "confirmation-server-destroy": "Existing users will lose access. This action cannot be undone.", + "confirmation-server-destroy-title": "Destroy Server?", + "confirmation-server-remove": "This action removes your server from the Outline Manager, but does not block proxy access to users. You will still need to manually delete the Outline server from your host machine.", + "confirmation-server-remove-title": "Remove Server?", + "destroy": "Destroy", + "digitalocean-disconnect": "Disconnect", + "digitalocean-disconnect-account": "Disconnect DigitalOcean account", + "digitalocean-unreachable": "This error may be due to a firewall on your network or temporary connectivity issues with digitalocean.com.", + "done": "Done", + "error-connectivity": "We're having trouble connecting to your DigitalOcean account. This is sometimes a temporary problem with DigitalOcean or with your internet connection. If retrying doesn't work, logging into DigitalOcean again should fix the problem.", + "error-connectivity-title": "Connection problem", + "error-do-account-info": "Failed to get DigitalOcean account information", + "error-do-auth": "Authentication with DigitalOcean failed", + "error-do-regions": "Failed to get list of available regions", + "error-feedback": "Failed to submit feedback. Please try again.", + "error-key-add": "Failed to add key", + "error-key-remove": "Failed to remove key", + "error-key-rename": "Failed to rename key", + "error-keys-get": "Could not load keys", + "error-licenses": "Could not load licenses.", + "error-metrics": "Error setting metrics enabled", + "error-server-creation": "There was an error creating your Outline server.", + "error-server-destroy": "Failed to destroy server", + "error-server-removed": "{serverName} no longer present in your DigitalOcean account.", + "error-server-rename": "Failed to rename server", + "error-server-unreachable": "Your Outline Server was installed correctly, but we are not able to connect to it. Most likely this is because your server's firewall rules are blocking incoming connections. Please review them and make sure to allow incoming TCP connections on ports ranging from 1024 to 65535.", + "error-server-unreachable-title": "Unable to connect to your Outline Server", + "error-servers-removed": "{serverNames} no longer present in your DigitalOcean account.", + "feedback-cloud-provider": "Select cloud provider", + "feedback-cloud-provider-error": "Please select a cloud provider.", + "feedback-connection": "Can't connect to my server", + "feedback-connection-others": "Others can't connect to my server", + "feedback-disclaimer": "Please note that our team is only able to answer feedback in English.", + "feedback-email": "Email address (optional)", + "feedback-error": "Please enter feedback.", + "feedback-explanation-install": "An error occurred while attempting to install Outline on your server. If you haven't been able to figure out a solution, please consider sending us feedback and telling us your email address (optional) so that we can get back to you.", + "feedback-general": "General feedback", + "feedback-install": "Having trouble installing Outline", + "feedback-label": "Your feedback", + "feedback-management": "Having trouble managing my server", + "feedback-other": "Other", + "feedback-privacy": "Your feedback, email address (if provided) and additional information referred to in the {openLink}privacy policy{closeLink} will be sent to the Outline team.", + "feedback-submit": "Submit", + "feedback-suggestion": "Suggestions", + "feedback-title-generic": "Send Feedback", + "feedback-title-install": "Outline Server Installation Failed", + "gcp-firewall-assign-0": "Navigate to the {openLink}VM instances screen{closeLink}.", + "gcp-firewall-assign-1": "Click on the instance where you want to host Outline.", + "gcp-firewall-assign-2": "Click 'EDIT' at the top of the VM Instance details screen.", + "gcp-firewall-assign-3": "Type 'outline' in the 'Network tags' field.", + "gcp-firewall-assign-4": "Click 'SAVE'.", + "gcp-firewall-create-0": "{openLink}Add a new firewall rule{closeLink} to your Compute Engine project.", + "gcp-firewall-create-1": "Type 'outline' in the 'Name' field.", + "gcp-firewall-create-2": "Type 'outline' in the 'Target tags' field.", + "gcp-firewall-create-3": "Type '0.0.0.0/0' in the 'Source IP ranges' field.", + "gcp-firewall-create-4": "Select 'Allow all' under 'Protocols and ports'.", + "gcp-firewall-create-5": "Click 'Create'.", + "key": "Key", + "manual-server-assign-firewall": "Assign firewall rule", + "manual-server-assign-group": "Assign Security Group", + "manual-server-create-firewall": "Create a firewall rule", + "manual-server-create-group": "Create a Security Group", + "manual-server-description": "These steps will help you install Outline on a {cloudProvider} Linux server.", + "manual-server-firewall": "Configure your firewall", + "manual-server-install-paste": "Paste your installation output here.", + "manual-server-install-run": "Log into your server, and run this command.", + "manual-server-instructions": "Instructions", + "manual-server-show-me": "Show me where", + "manual-server-title": "Follow the instructions below", + "metrics-description": "Share anonymized metrics to help improve the reliability and performance of Outline, for you and for those you share your server with. {openLink}Learn more.{closeLink}", + "metrics-share": "Share metrics", + "metrics-skip": "Skip", + "metrics-title": "Metrics sharing", + "nav-about": "About", + "nav-data-collection": "Data collection", + "nav-feedback": "Feedback", + "nav-help": "Help", + "nav-licenses": "Licenses", + "nav-privacy": "Privacy", + "nav-terms": "Terms", + "notification-app-update": "An updated version of the Outline Manager has been downloaded. It will be installed when you restart the application.", + "notification-feedback-thanks": "Thanks for helping us improve! We love hearing from you.", + "notification-key-added": "Key added", + "notification-key-removed": "Key removed", + "notification-server-destroyed": "Server destroyed", + "notification-server-exists": "Server already added", + "notification-server-removed": "Server removed", + "oauth-account-active": "Your DigitalOcean account has been activated.", + "oauth-account-active-tag": "Account activated! Loading server locations...", + "oauth-activate-account": "Activate you DigitalOcean account.", + "oauth-billing": "Enter your billing information on digitalocean.com and return to the app once you are done.", + "oauth-billing-tag": "Enter billing information...", + "oauth-connect-description": "With your account, Outline makes it easy to create a server and get connected.", + "oauth-connect-tag": "Waiting to connect your account...", + "oauth-connect-title": "Sign in or create an account with DigitalOcean.", + "oauth-sign-out": "Sign Out", + "oauth-verify": "Check your inbox for an email from DigitalOcean, and click the link in it to confirm your account.", + "oauth-verify-tag": "Confirm your email...", + "oauth-verify-title": "Activate you DigitalOcean account.", + "okay": "OK", + "region-description": "This is where your internet experience will come from.", + "region-setup": "Set up Outline", + "region-title": "Select the location of your server.", + "remove": "Remove", + "retry": "Retry", + "server-access": "Server access", + "server-access-key-new": "Add new key", + "server-access-key-rename": "Rename", + "server-access-keys": "Access keys", + "server-connect-devices": "Connect your devices", + "server-connections": "Connections", + "server-data-transfer": "Data transferred / 30 days", + "server-data-used": "Allowance used / 30 days", + "server-destroy": "Destroy server", + "server-help-access-key-description": "Share access keys with friends, so they can connect to your Outline server. They can use the same access key on all their devices.", + "server-help-access-key-next": "Next", + "server-help-access-key-title": "Create keys, share access", + "server-help-connection-description": "Click here to install the Outline client app, using your personal access key to your Outline server.", + "server-help-connection-ok": "Okay, got it!", + "server-help-connection-title": "You are not connected yet!", + "server-keys": "Keys", + "server-my-access-key": "My access key", + "server-name": "Outline Server {serverLocation}", + "server-remove": "Remove server", + "server-settings": "Settings", + "server-unreachable": "Server unreachable", + "server-unreachable-description": "We're having issues connecting to this server.", + "server-unreachable-managed-description": "Try again or remove this server from the application.", + "server-unreachable-manual-description": "Try again or destroy this server and the virtual host.", + "server-usage": "Usage / 30 days", + "servers-add": "Add server", + "servers-digitalocean": "DigitalOcean servers", + "servers-manual": "Servers", + "settings-access-key-port": "Port for new access keys", + "settings-metrics-header": "Share anonymous metrics", + "settings-server-api-url": "Management API URL", + "settings-server-cost": "Monthly cost", + "settings-server-creation": "Created", + "settings-server-hostname": "Hostname", + "settings-server-id": "Server ID", + "settings-server-info": "Server Information", + "settings-server-location": "Server location", + "settings-server-name": "Name", + "settings-server-rename": "Set a new name for your server. Note that this will not be reflected on the devices of the users that you invited to connect to it.", + "settings-transfer-limit": "Data transfer allowance", + "setup-action": "Set up", + "setup-advanced": "Advanced", + "setup-anywhere": "Set up Outline anywhere", + "setup-create": "Create server", + "setup-description": "Don't have a server? Create an account with DigitalOcean.", + "setup-do-cancel": "Cancel at anytime", + "setup-do-cost": "Only US$5 a month", + "setup-do-create": "Create a new server with your DigitalOcean account for an additional US$5/30 days for 1 TB of data transfer.", + "setup-do-data": "1 TB data transfer allowance", + "setup-do-description": "This could take up to two minutes. You can destroy this server at anytime.", + "setup-do-easiest": "Easiest setup process", + "setup-do-title": "Setting up Outline.", + "setup-firewall-instructions": "Firewall instructions", + "setup-recommended": "Recommended", + "setup-simple-commands": "Simple install commands", + "setup-step-by-step": "Step-by-step set-up guide", + "setup-tested": "Tested on VULTR, Linode, and Liquid Web", + "setup-title": "Choose a cloud service to set up Outline.", + "share-description": "Copy this invitation and send it from a communication tool you trust. {openLink}Need help?{closeLink}", + "share-invite": "You\u2019re invited to connect to my Outline server. Use it to access the open internet, no matter where you are. Follow the instructions on your invitation link below to download the Outline App and get connected.", + "share-invite-copied": "Copied to clipboard", + "share-invite-copy": "Copy invitation", + "share-invite-copy-access-key": "Copy your access key:", + "share-invite-instructions": "Follow our invitation instructions on GitHub:", + "share-invite-trouble": "Having trouble accessing the invitation link?", + "share-title": "Share access", + "terms-of-service": "I have read and understood the {openLink}Outline Terms of Service{closeLink}" +} diff --git a/src/server_manager/messages/master_messages.json b/src/server_manager/messages/master_messages.json new file mode 100644 index 00000000..3fdeb353 --- /dev/null +++ b/src/server_manager/messages/master_messages.json @@ -0,0 +1,892 @@ +{ + "about_outline": { + "message": "Outline is an open source project created by $JIGSAW$ to provide a safer way for news organizations and journalists to access the internet.$NEW_LINE$$NEW_LINE$ Outline is powered by $SHADOWSOCKS$ and is still an early stage product. You can contribute to the code on $GITHUB$, and follow us on $REDDIT$ and $MEDIUM$ to hear when we expand to more platforms and add new features.", + "description": "This string appears in a dialog as a paragraph to provide a description of the product. Outline is the product name and should not be translated.", + "placeholders": { + "GITHUB": { + "content": "GitHub" + }, + "JIGSAW": { + "content": "Jigsaw" + }, + "MEDIUM": { + "content": "Medium" + }, + "NEW_LINE": { + "content": "
" + }, + "REDDIT": { + "content": "Reddit" + }, + "SHADOWSOCKS": { + "content": "Shadowsocks" + } + } + }, + "about_version": { + "message": "Version $VERSION$", + "description": "This string appears in a dialog as a header to indicate the version of the application.", + "placeholders": { + "VERSION": { + "content": "{version}", + "example": "1.0.2" + } + } + }, + "aws_group_assign_0": { + "message": "Navigate to $START_OF_LINK$Instances$END_OF_LINK$.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services.", + "placeholders": { + "END_OF_LINK": { + "content": "{closeLink}" + }, + "START_OF_LINK": { + "content": "{openLink}" + } + } + }, + "aws_group_assign_1": { + "message": "Click 'Actions' > 'Networking' > 'Change Security Groups' at the top of the 'Instances' screen.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. Words in quotes should be translated and included untranslated in parentheses." + }, + "aws_group_assign_2": { + "message": "Select the 'outline' Security Group, unselect other security groups.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. 'Security Group' should be translated and included untranslated in parentheses." + }, + "aws_group_assign_3": { + "message": "Click 'Assign Security Groups'.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. 'Assign Security Groups' should be translated and included untranslated in parentheses." + }, + "aws_group_create_0": { + "message": "Once you've created your EC2 instance, open the $START_OF_LINK$EC2 Management Console$END_OF_LINK$.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. EC2 is a product name and should not be translated.", + "placeholders": { + "END_OF_LINK": { + "content": "{closeLink}" + }, + "START_OF_LINK": { + "content": "{openLink}" + } + } + }, + "aws_group_create_1": { + "message": "Under 'Network & Security', select 'Security Groups'.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. Phrases in quotes should be translated and included untranslated in parentheses." + }, + "aws_group_create_2": { + "message": "Click on 'Create Security Group'.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. 'Create Security Group' should be translated and included untranslated in parentheses." + }, + "aws_group_create_3": { + "message": "Type 'outline' in the 'Security group name' and 'Description' fields.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. Phrases in quotes should be translated and included untranslated in parentheses." + }, + "aws_group_create_4": { + "message": "Click 'Add Rule'.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. 'Add rule' should be translated and included untranslated in parentheses." + }, + "aws_group_create_5": { + "message": "Select 'All traffic' under 'Type', and 'Anywhere' under 'Source'.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a security group in Amazon Web Services. Phrases in quotes should be translated and included untranslated in parentheses." + }, + "cancel": { + "message": "Cancel", + "description": "This string appears across the application as a button. Clicking it aborts the current flow." + }, + "city_ams": { + "message": "Amsterdam", + "description": "This string appears in a card as part of the server creation flow. It allows the user to select the location to deploy a server. Refers to the city in the Netherlands." + }, + "city_blr": { + "message": "Bangalore", + "description": "This string appears in a card as part of the server creation flow. It allows the user to select the location to deploy a server. Refers to the city in India." + }, + "city_fra": { + "message": "Frankfurt", + "description": "This string appears in a card as part of the server creation flow. It allows the user to select the location to deploy a server. Refers to the city in Germany." + }, + "city_lon": { + "message": "London", + "description": "This string appears in a card as part of the server creation flow. It allows the user to select the location to deploy a server. Refers to the city in England." + }, + "city_nyc": { + "message": "New York", + "description": "This string appears in a card as part of the server creation flow. It allows the user to select the location to deploy a server. Refers to the city in the United States." + }, + "city_sfo": { + "message": "San Francisco", + "description": "This string appears in a card as part of the server creation flow. It allows the user to select the location to deploy a server. Refers to the city in the United States." + }, + "city_sgp": { + "message": "Singapore", + "description": "This string appears in a card as part of the server creation flow. It allows the user to select the location to deploy a server. Refers to the capital of Singapore." + }, + "city_tor": { + "message": "Toronto", + "description": "This string appears in a card as part of the server creation flow. It allows the user to select the location to deploy a server. Refers to the city in Canada." + }, + "close": { + "message": "Close", + "description": "This string appears in dialogs as a button. Clicking it dismisses the dialog." + }, + "confirmation_server_destroy": { + "message": "Existing users will lose access. This action cannot be undone.", + "description": "This string appears in a dialog that requests user confirmation for destroying a server. It informs the user about the consequences of destroying a server. 'Destroy' in this context implies that the server will be deleted." + }, + "confirmation_server_destroy_title": { + "message": "Destroy Server?", + "description": "This string appears in a dialog that requests user confirmation for destroying a server. 'Destroy' in this context implies that the server will be deleted." + }, + "confirmation_server_remove": { + "message": "This action removes your server from the Outline Manager, but does not block proxy access to users. You will still need to manually delete the Outline server from your host machine.", + "description": "This string appears in a dialog that requests user confirmation for removing a server from the application. It informs the user about the consequences of removing a server. 'Remove' in this context does not imply server deletion." + }, + "confirmation_server_remove_title": { + "message": "Remove Server?", + "description": "This string appears in a dialog that requests user confirmation for removing a server from the application. 'Remove' in this context does not imply server deletion." + }, + "destroy": { + "message": "Destroy", + "description": "This string appears as a button in a dialog that requests user confirmation for destroying a server. Clicking the button causes the server to be deleted." + }, + "digitalocean_disconnect": { + "message": "Disconnect", + "description": "This string appears as a button in a dialog to disconnect the user's DigitalOcean account from the application. Clicking it signs the user out of DigitalOcean, a cloud server provider." + }, + "digitalocean_disconnect_account": { + "message": "Disconnect DigitalOcean account", + "description": "This string appears as a header in a dialog to disconnect the user's DigitalOcean account from the application. DigitalOcean is a cloud server provider name and should not be translated." + }, + "digitalocean_unreachable": { + "message": "This error may be due to a firewall on your network or temporary connectivity issues with digitalocean.com.", + "description": "This string appears in a dialog as a paragraph. It is shown when a DigitalOcean server cannot be reached. DigitalOcean is a cloud server provider." + }, + "done": { + "message": "Done", + "description": "This string appears across the application as a button. It lets the user indicate that an action, such as entering text or reading a dialog, has been completed." + }, + "error_connectivity": { + "message": "We're having trouble connecting to your DigitalOcean account. This is sometimes a temporary problem with DigitalOcean or with your internet connection. If retrying doesn't work, logging into DigitalOcean again should fix the problem.", + "description": "This string appears in a dialog as a paragraph. The dialog is shown when an operation on a DigitalOcean server fails due to connectivity issues. The dialog displays buttons that allow the user to retry the operation. DigitalOcean is a cloud server provider name and should not be translated." + }, + "error_connectivity_title": { + "message": "Connection problem", + "description": "This string appears in a dialog as a header. The dialog is shown when an operation on a server fails due to connectivity issues. The dialog displays buttons that allow the user to retry the operation." + }, + "error_do_account_info": { + "message": "Failed to get DigitalOcean account information", + "description": "This string appears in an error notification toast. It is shown when there is an error retrieving the user's DigitalOcean account. DigitalOcean is a cloud server provider name and should not be translated." + }, + "error_do_auth": { + "message": "Authentication with DigitalOcean failed", + "description": "This string appears in an error notification toast. It is shown when there is an error when logging in to the user's DigitalOcean account. DigitalOcean is a cloud server provider name and should not be translated." + }, + "error_do_regions": { + "message": "Failed to get list of available regions", + "description": "This string appears in an error notification toast. It is shown when there is an error retrieving the regions available for server deployment." + }, + "error_feedback": { + "message": "Failed to submit feedback. Please try again.", + "description": "This string appears in an error notification toast. It is shown when there is an error submitting the user's feedback." + }, + "error_key_add": { + "message": "Failed to add key", + "description": "This string appears in an error notification toast. It is shown when there is an error creating a server access key." + }, + "error_key_remove": { + "message": "Failed to remove key", + "description": "This string appears in an error notification toast. It is shown when there is an error deleting a server access key." + }, + "error_key_rename": { + "message": "Failed to rename key", + "description": "This string appears in an error notification toast. It is shown when there is an error renaming a server access key." + }, + "error_keys_get": { + "message": "Could not load keys", + "description": "This string appears in an error notification toast. It is shown when there is an error retrieving a server access keys." + }, + "error_licenses": { + "message": "Could not load licenses.", + "description": "This string appears in a dialog that shows the application's software licenses. It is shown instead of the licenses' text when loading them fails." + }, + "error_metrics": { + "message": "Error setting metrics enabled", + "description": "This string appears in an error notification toast. It is shown when there is an error enabling or disabling a server's metrics reporting." + }, + "error_server_creation": { + "message": "There was an error creating your Outline server.", + "description": "This string appears in an dialog as a paragraph. The dialog is shown when there is an creating a server; the user can retry the operation or destroy the server." + }, + "error_server_destroy": { + "message": "Failed to destroy server", + "description": "This string appears in an error notification toast. It is shown when there is an error destroying a server." + }, + "error_server_removed": { + "message": "$SERVER_NAME$ no longer present in your DigitalOcean account.", + "description": "This string appears in an error notification toast. It is shown when a DigitalOcean server was destroyed outside the application to let the user know that it will not be displayed in the UI. DigitalOcean is a cloud server provider name and should not be translated.", + "placeholders": { + "SERVER_NAME": { + "content": "{serverName}", + "example": "New York Outline Server" + } + } + }, + "error_server_rename": { + "message": "Failed to rename server", + "description": "This string appears in an error notification toast. It is shown when there is an error renaming a server." + }, + "error_server_unreachable": { + "message": "Your Outline Server was installed correctly, but we are not able to connect to it. Most likely this is because your server's firewall rules are blocking incoming connections. Please review them and make sure to allow incoming TCP connections on ports ranging from 1024 to 65535.", + "description": "This string appears in dialog as a paragraph. The dialog is shown when a server is installed successfully but cannot be reached. The dialog provides possible solutions and displays buttons that allow the user to retry reaching the server." + }, + "error_server_unreachable_title": { + "message": "Unable to connect to your Outline Server", + "description": "This string appears in dialog as a header. The dialog is shown when a server is installed successfully but cannot be reached." + }, + "error_servers_removed": { + "message": "$SERVER_NAMES$ no longer present in your DigitalOcean account.", + "description": "This string appears in an error notification toast. It is shown when multiple DigitalOcean servers were destroyed outside the application to let the user know that they will not be displayed in the UI. DigitalOcean is a cloud server provider name and should not be translated.", + "placeholders": { + "SERVER_NAMES": { + "content": "{serverNames}", + "example": "New York Outline Server, Amsterdam Outline Server" + } + } + }, + "feedback_cloud_provider": { + "message": "Select cloud provider", + "description": "This string appears in the feedback dialog as an placeholder within a drop-down. Allows the user to select a cloud provider for certain feedback categories." + }, + "feedback_cloud_provider_error": { + "message": "Please select a cloud provider.", + "description": "This string appears in the feedback dialog as an error message within a drop-down. The string appears when the user attempts to submit feedback without selecting a cloud provider." + }, + "feedback_connection": { + "message": "Can't connect to my server", + "description": "This string appears in the feedback dialog as an option within a drop-down of feedback categories. Indicates that the user cannot connect to their server." + }, + "feedback_connection_others": { + "message": "Others can't connect to my server", + "description": "This string appears in the feedback dialog as an option within a drop-down of feedback categories. Indicates that users who have been given access keys cannot connect to the server." + }, + "feedback_disclaimer": { + "message": "Please note that our team is only able to answer feedback in English.", + "description": "This string appears in the feedback dialog as a paragraph under the feedback form. Lets the user know that, although the application is localized, our team can only respond to feedback in English." + }, + "feedback_email": { + "message": "Email address (optional)", + "description": "This string appears in the feedback dialog as a label of the email input form." + }, + "feedback_error": { + "message": "Please enter feedback.", + "description": "This string appears in the feedback dialog as a paragraph below the feedback input form. It is displayed when the user clicks the submit button without having entered feedback in the input form." + }, + "feedback_explanation_install": { + "message": "An error occurred while attempting to install Outline on your server. If you haven't been able to figure out a solution, please consider sending us feedback and telling us your email address (optional) so that we can get back to you.", + "description": "This string appears in the feedback dialog as a paragraph above the feedback input form. The feedback dialog gets automatically displayed with this message when a server installation fails." + }, + "feedback_general": { + "message": "General feedback", + "description": "This string appears in the feedback dialog as an option within a drop-down of feedback categories. Indicates that the user is providing general feedback." + }, + "feedback_install": { + "message": "Having trouble installing Outline", + "description": "This string appears in the feedback dialog as an option within a drop-down of feedback categories. Indicates that the user cannot install Outline on their server." + }, + "feedback_label": { + "message": "Your feedback", + "description": "This string appears in the feedback dialog as a placeholder of the feedback input form." + }, + "feedback_management": { + "message": "Having trouble managing my server", + "description": "This string appears in the feedback dialog as an option within a drop-down of feedback categories. Indicates that the user is having issues managing their server." + }, + "feedback_other": { + "message": "Other", + "description": "This string appears in the feedback dialog as an option within a drop-down of cloud providers. Indicates that the user has a server on an unlisted cloud provider." + }, + "feedback_privacy": { + "message": "Your feedback, email address (if provided) and additional information referred to in the $START_OF_LINK$privacy policy$END_OF_LINK$ will be sent to the Outline team.", + "description": "This string appears in the feedback dialog as paragraph below the feedback input form. Lets the user know how their feedback and personal information will be handled.", + "placeholders": { + "END_OF_LINK": { + "content": "{closeLink}" + }, + "START_OF_LINK": { + "content": "{openLink}" + } + } + }, + "feedback_submit": { + "message": "Submit", + "description": "This string appears in the feedback dialog as a button. Clicking it submits the feedback form." + }, + "feedback_suggestion": { + "message": "Suggestions", + "description": "This string appears in the feedback dialog as an option within a drop-down of feedback categories. Indicates that the user is providing a product suggestion." + }, + "feedback_title_generic": { + "message": "Send Feedback", + "description": "This string appears in the feedback dialog as a header." + }, + "feedback_title_install": { + "message": "Outline Server Installation Failed", + "description": "This string appears in the feedback dialog as a header. The feedback dialog gets automatically displayed with this header when a server installation fails." + }, + "gcp_firewall_assign_0": { + "message": "Navigate to the $START_OF_LINK$VM instances screen$END_OF_LINK$.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform.", + "placeholders": { + "END_OF_LINK": { + "content": "{closeLink}" + }, + "START_OF_LINK": { + "content": "{openLink}" + } + } + }, + "gcp_firewall_assign_1": { + "message": "Click on the instance where you want to host Outline.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform." + }, + "gcp_firewall_assign_2": { + "message": "Click 'EDIT' at the top of the VM Instance details screen.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. 'EDIT' should be translated and included untranslated in parentheses. VM is an acronym for virtual machine." + }, + "gcp_firewall_assign_3": { + "message": "Type 'outline' in the 'Network tags' field.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. 'Network tags' should be translated and included untranslated in parentheses." + }, + "gcp_firewall_assign_4": { + "message": "Click 'SAVE'.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. 'SAVE' should be translated and included untranslated in parentheses." + }, + "gcp_firewall_create_0": { + "message": "$START_OF_LINK$Add a new firewall rule$END_OF_LINK$ to your Compute Engine project.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. Compute Engine is a product of Google Cloud Platform and should not be translated.", + "placeholders": { + "END_OF_LINK": { + "content": "{closeLink}" + }, + "START_OF_LINK": { + "content": "{openLink}" + } + } + }, + "gcp_firewall_create_1": { + "message": "Type 'outline' in the 'Name' field.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. 'Name' should be translated and included untranslated in parentheses." + }, + "gcp_firewall_create_2": { + "message": "Type 'outline' in the 'Target tags' field.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. 'Target tags' should be translated and included untranslated in parentheses." + }, + "gcp_firewall_create_3": { + "message": "Type '0.0.0.0/0' in the 'Source IP ranges' field.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. 'Source IP ranges' should be translated and included untranslated in parentheses." + }, + "gcp_firewall_create_4": { + "message": "Select 'Allow all' under 'Protocols and ports'.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. 'Allow all' and 'Protocols and ports' should be translated and included untranslated in parentheses." + }, + "gcp_firewall_create_5": { + "message": "Click 'Create'.", + "description": "This string appears within the server setup view as an item of a list that provides instructions to configure a firewall in Google Cloud Platform. 'Create' should be translated and included untranslated in parentheses." + }, + "key": { + "message": "Key", + "description": "This string appears in the server view as a placeholder for a newly created access key." + }, + "manual_server_assign_firewall": { + "message": "Assign firewall rule", + "description": "This string appears in the server setup view as a sub-header of a section that provides instructions to configure the server's firewall." + }, + "manual_server_assign_group": { + "message": "Assign Security Group", + "description": "This string appears in the server setup view as a sub-header of a section that provides instructions to configure the server's firewall." + }, + "manual_server_create_firewall": { + "message": "Create a firewall rule", + "description": "This string appears in the server setup view as a sub-header of a section that provides instructions to configure the server's firewall." + }, + "manual_server_create_group": { + "message": "Create a Security Group", + "description": "This string appears in the server setup view as a sub-header of a section that provides instructions to configure the server's firewall." + }, + "manual_server_description": { + "message": "These steps will help you install Outline on a $CLOUD_PROVIDER$ Linux server.", + "description": "This string appears in the server setup view as a header. Lets the user know that the following sections provide instructions on how to install Outline on their server.", + "placeholders": { + "CLOUD_PROVIDER": { + "content": "{cloudProvider}", + "example": "Amazon Web Services" + } + } + }, + "manual_server_firewall": { + "message": "Configure your firewall", + "description": "This string appears in the server setup view as the header of a section that provides instructions to configure the server's firewall." + }, + "manual_server_install_paste": { + "message": "Paste your installation output here.", + "description": "This string appears in the server setup view as a header of a section that contains an input form. The user must enter the output of the server installation script to manage their server in the application." + }, + "manual_server_install_run": { + "message": "Log into your server, and run this command.", + "description": "This string appears in the server setup view as a header of a section that displays a command to install Outline on their server." + }, + "manual_server_instructions": { + "message": "Instructions", + "description": "This string appears in the server setup view as a toggle of a section that provides instructions to configure the server's firewall." + }, + "manual_server_show_me": { + "message": "Show me where", + "description": "This string appears in the server setup view as a button. Clicking the button opens a link to the cloud server provider management console." + }, + "manual_server_title": { + "message": "Follow the instructions below", + "description": "This string appears in the server setup view as a header. Communicates to the user that they need to perform a series of actions to install Outline on their server." + }, + "metrics_description": { + "message": "Share anonymized metrics to help improve the reliability and performance of Outline, for you and for those you share your server with. $START_OF_LINK$Learn more.$END_OF_LINK$", + "description": "This string appears in the server settings view as a paragraph next to a toggle that enables/disables server metrics reporting.", + "placeholders": { + "END_OF_LINK": { + "content": "{closeLink}" + }, + "START_OF_LINK": { + "content": "{openLink}" + } + } + }, + "metrics_share": { + "message": "Share metrics", + "description": "This string appears as a button of a dialog that prompts the user to share server metrics. Clicking it enables server metrics reporting." + }, + "metrics_skip": { + "message": "Skip", + "description": "This string appears as a button of a dialog that prompts the user to share server metrics. Clicking it dismisses the dialog." + }, + "metrics_title": { + "message": "Metrics sharing", + "description": "This string appears as the header of a dialog that prompts the user to share server metrics." + }, + "nav_about": { + "message": "About", + "description": "This string appears in an application drawer as a navigation link. Clicking it opens a dialog with information about Outline." + }, + "nav_data_collection": { + "message": "Data collection", + "description": "This string appears in an application drawer as a navigation link. Clicking it opens Outline's data collection policy in the browser." + }, + "nav_feedback": { + "message": "Feedback", + "description": "This string appears in an application drawer as a navigation link. Clicking it opens a dialog that allows the user to submit feedback." + }, + "nav_help": { + "message": "Help", + "description": "This string appears in an application drawer as a navigation link. Clicking it opens Outline's support website." + }, + "nav_licenses": { + "message": "Licenses", + "description": "This string appears in an application drawer as a navigation link. Clicking it opens a dialog listing Outline's software licenses." + }, + "nav_privacy": { + "message": "Privacy", + "description": "This string appears in an application drawer as a navigation link. Clicking it opens Outline's privacy policy in the browser." + }, + "nav_terms": { + "message": "Terms", + "description": "This string appears in an application drawer as a navigation link. Clicking it opens Outline's terms of service in the browser." + }, + "notification_app_update": { + "message": "An updated version of the Outline Manager has been downloaded. It will be installed when you restart the application.", + "description": "This string appears in a transient notification toast. It is shown when an update to the application is ready to install." + }, + "notification_feedback_thanks": { + "message": "Thanks for helping us improve! We love hearing from you.", + "description": "This string appears in a transient notification toast. It is shown when the user has successfully submitted feedback." + }, + "notification_key_added": { + "message": "Key added", + "description": "This string appears in a transient notification toast. It is shown when a server access key has been successfully created." + }, + "notification_key_removed": { + "message": "Key removed", + "description": "This string appears in a transient notification toast. It is shown when a server access key has been successfully removed." + }, + "notification_server_destroyed": { + "message": "Server destroyed", + "description": "This string appears in a transient notification toast. It is shown when a server has been successfully destroyed." + }, + "notification_server_exists": { + "message": "Server already added", + "description": "This string appears in a transient notification toast. It is shown when the user attempts to add server that is already present in the application." + }, + "notification_server_removed": { + "message": "Server removed", + "description": "This string appears in a transient notification toast. It is shown when a server has been successfully removed from the application." + }, + "oauth_account_active": { + "message": "Your DigitalOcean account has been activated.", + "description": "This string appears in the DigitalOcean account creation flow as a sub-header. Lets the user know their DigitalOcean account is active. DigitalOcean is a cloud provider name and should not be translated." + }, + "oauth_account_active_tag": { + "message": "Account activated! Loading server locations...", + "description": "This string appears in the DigitalOcean account creation flow as a paragraph. Displayed when the user's DigitalOcean account has been successfully activated." + }, + "oauth_activate_account": { + "message": "Activate you DigitalOcean account.", + "description": "This string appears in the DigitalOcean account creation flow as a header. Displayed when the account has been created but not yet active. DigitalOcean is a cloud provider name and should not be translated." + }, + "oauth_billing": { + "message": "Enter your billing information on digitalocean.com and return to the app once you are done.", + "description": "This string appears in the DigitalOcean account creation flow as a sub-header. Prompts the user to enter their billing information on DigitalOcean's website." + }, + "oauth_billing_tag": { + "message": "Enter billing information...", + "description": "This string appears in the DigitalOcean account creation flow as a paragraph. Prompts the user to enter their billing information on DigitalOcean's website." + }, + "oauth_connect_description": { + "message": "With your account, Outline makes it easy to create a server and get connected.", + "description": "This string appears in the DigitalOcean account creation flow as a sub-header. Displayed when the user has not yet authorized Outline to access their DigitalOcean account." + }, + "oauth_connect_tag": { + "message": "Waiting to connect your account...", + "description": "This string appears in the DigitalOcean account creation flow as a paragraph. Displayed when the user has not yet authorized Outline to access their DigitalOcean account." + }, + "oauth_connect_title": { + "message": "Sign in or create an account with DigitalOcean.", + "description": "This string appears in the DigitalOcean account creation flow as a header. Displayed when the user has not yet signed in to DigitalOcean or created an account." + }, + "oauth_sign_out": { + "message": "Sign Out", + "description": "This string appears in the DigitalOcean account creation flow as a button. Displayed when the user has authorized Outline to access their account. Clicking it aborts the account creation flow and signs out the user." + }, + "oauth_verify": { + "message": "Check your inbox for an email from DigitalOcean, and click the link in it to confirm your account.", + "description": "This string appears in the DigitalOcean account creation flow as a sub-header. Displayed when the user has not yet entered billing information to activate their DigitalOcean account. DigitalOcean is a cloud provider name and should not be translated." + }, + "oauth_verify_tag": { + "message": "Confirm your email...", + "description": "This string appears in the DigitalOcean account creation flow as a paragraph. Displayed when the user has not yet confirmed the registered email for their DigitalOcean account." + }, + "oauth_verify_title": { + "message": "Activate you DigitalOcean account.", + "description": "This string appears in the DigitalOcean account creation flow as a title. Displayed when the user has not yet activated their DigitalOcean account." + }, + "okay": { + "message": "OK", + "description": "This string appears across the application as a button. It lets the user acknowledge displayed information. Clicking dismisses the enclosing UI element." + }, + "region_description": { + "message": "This is where your internet experience will come from.", + "description": "This string appears within the server creation flow, as a sub-header of the server selection view. Lets the user know about the implications of selecting a server location." + }, + "region_setup": { + "message": "Set up Outline", + "description": "This string appears within the server creation flow, as a button of the server selection view. Clicking it creates an Outline server at the selected location. 'Set up' means install and run the Outline server software." + }, + "region_title": { + "message": "Select the location of your server.", + "description": "This string appears within the server creation flow, as the header of the server selection view." + }, + "remove": { + "message": "Remove", + "description": "This string appears across the application as a button. It allows the user to confirm the removal of a server or access key." + }, + "retry": { + "message": "Retry", + "description": "This string appears across the application as a button. It allows the user to retry a failed operation." + }, + "server_access": { + "message": "Server access", + "description": "This string appears within the server view as the header of a card that displays the number of server access keys." + }, + "server_access_key_new": { + "message": "Add new key", + "description": "This string appears within the server view as a button. Clicking it creates a new server access key." + }, + "server_access_key_rename": { + "message": "Rename", + "description": "This string appears within the server view as a button of a drop-down to manage a server access key. Clicking it renames the access key." + }, + "server_access_keys": { + "message": "Access keys", + "description": "This string appears within the server view as a header of a table column that displays server access keys." + }, + "server_connect_devices": { + "message": "Connect your devices", + "description": "This string appears within the server view as a sub-header for the default server access key. This key is meant to be used by the server administrator." + }, + "server_connections": { + "message": "Connections", + "description": "This string appears within the server view as a header of the section that displays server information and access keys." + }, + "server_data_transfer": { + "message": "Data transferred / 30 days", + "description": "This string appears within the server view as the header of a card that displays the amount of data transferred by the server." + }, + "server_data_used": { + "message": "Allowance used / 30 days", + "description": "This string appears within the server view as the header of a card that displays the amount of data transferred by the server as a percentage of the total available data." + }, + "server_destroy": { + "message": "Destroy server", + "description": "This string appears within the server view as a button of a drop-down to manage a server. Clicking it opens a confirmation dialog to destroy the server." + }, + "server_help_access_key_description": { + "message": "Share access keys with friends, so they can connect to your Outline server. They can use the same access key on all their devices.", + "description": "This string appears within a help bubble as a paragraph. The help bubble is displayed when a server is installed. Informs to the user how to share access to their server." + }, + "server_help_access_key_next": { + "message": "Next", + "description": "This string appears within a help bubble as a button. Clicking it displays the next help bubble." + }, + "server_help_access_key_title": { + "message": "Create keys, share access", + "description": "This string appears within a help bubble as a header. The help bubble provides information about sharing access to the server through access keys." + }, + "server_help_connection_description": { + "message": "Click here to install the Outline client app, using your personal access key to your Outline server.", + "description": "This string appears within a help bubble as a paragraph. Informs the user how to install the Outline clients and get connected to the server." + }, + "server_help_connection_ok": { + "message": "Okay, got it!", + "description": "This string appears within a help bubble as a button. Clicking it dismisses the help bubble." + }, + "server_help_connection_title": { + "message": "You are not connected yet!", + "description": "This string appears within a help bubble as a header. The help bubble provides information about connecting to the server." + }, + "server_keys": { + "message": "Keys", + "description": "This string appears within the server view as the unit of a card that displays the number of server access keys." + }, + "server_my_access_key": { + "message": "My access key", + "description": "This string appears within the server view as the header for the default server access key. This key is meant to be used by the server administrator." + }, + "server_name": { + "message": "Outline Server $SEVER_LOCATION$", + "description": "This string appears within the server view and in the application drawer. It is the default name for a newly created server.", + "placeholders": { + "SEVER_LOCATION": { + "content": "{serverLocation}", + "example": "New York" + } + } + }, + "server_remove": { + "message": "Remove server", + "description": "This string appears within the server view as a button of a drop-down to manage a server. Clicking it opens a confirmation dialog to remove the server from the application." + }, + "server_settings": { + "message": "Settings", + "description": "This string appears within the server view as a header of the section that displays server settings." + }, + "server_unreachable": { + "message": "Server unreachable", + "description": "This string appears within a server view as a header. Displayed when a server's information cannot be displayed because it is not reachable." + }, + "server_unreachable_description": { + "message": "We're having issues connecting to this server.", + "description": "This string appears within a server view as a paragraph. Informs the user that a server's information cannot be displayed because it is not reachable." + }, + "server_unreachable_managed_description": { + "message": "Try again or remove this server from the application.", + "description": "This string appears within a server view as a header. Displayed when a server's information cannot be displayed because it is not reachable. Prompts the user to retry contacting the server or remove it from the application." + }, + "server_unreachable_manual_description": { + "message": "Try again or destroy this server and the virtual host.", + "description": "This string appears within a server view as a header. Displayed when a server's information cannot be displayed because it is not reachable. Prompts the user to retry contacting the server or destroy it." + }, + "server_usage": { + "message": "Usage / 30 days", + "description": "This string appears within the server view as a header of a table column that displays the breakdown of data usage by access key." + }, + "servers_add": { + "message": "Add server", + "description": "This string appears in an application drawer as a button. Clicking it starts the server setup flow." + }, + "servers_digitalocean": { + "message": "DigitalOcean servers", + "description": "This string appears in an application drawer as the header of a section that displays the list of DigitalOcean servers. DigitalOcean is a cloud server provider name and should not be translated." + }, + "servers_manual": { + "message": "Servers", + "description": "This string appears in an application drawer as the header of a section that displays a list of servers." + }, + "settings_access_key_port": { + "message": "Port for new access keys", + "description": "This string appears in the server settings view as a label for the server port number used to create new access keys." + }, + "settings_metrics_header": { + "message": "Share anonymous metrics", + "description": "This string appears in the server settings view as a header of the section to configure server metrics reporting." + }, + "settings_server_api_url": { + "message": "Management API URL", + "description": "This string appears in the server settings view as a label for the server's management API URL. API stands for application programming interface; the acronym should not be translated." + }, + "settings_server_cost": { + "message": "Monthly cost", + "description": "This string appears in the server settings view as a label for the server's monthly cost." + }, + "settings_server_creation": { + "message": "Created", + "description": "This string appears in the server settings view as a label for the server's creation date." + }, + "settings_server_hostname": { + "message": "Hostname", + "description": "This string appears in the server settings view as a label for the server's IP address or domain name." + }, + "settings_server_id": { + "message": "Server ID", + "description": "This string appears in the server settings view as a label for the server's unique identifier." + }, + "settings_server_info": { + "message": "Server Information", + "description": "This string appears in the server settings view as a header of the section that displays server details." + }, + "settings_server_location": { + "message": "Server location", + "description": "This string appears in the server settings view as a label for the server's location." + }, + "settings_server_name": { + "message": "Name", + "description": "This string appears in the server settings view as a label for the server's name." + }, + "settings_server_rename": { + "message": "Set a new name for your server. Note that this will not be reflected on the devices of the users that you invited to connect to it.", + "description": "This string appears in the server settings view as a paragraph below the server's name. Informs the user about the implications of renaming a server." + }, + "settings_transfer_limit": { + "message": "Data transfer allowance", + "description": "This string appears in the server settings view as a label for the server's data transfer allowance." + }, + "setup_action": { + "message": "Set up", + "description": "This string appears in the server setup view as a button. Clicking it starts the server setup flow. 'Set up' means install and run the Outline server software." + }, + "setup_advanced": { + "message": "Advanced", + "description": "This string appears in the server setup view as a label. Advanced in this context implies that the user will have to follow technical instructions to install Outline." + }, + "setup_anywhere": { + "message": "Set up Outline anywhere", + "description": "This string appears in the server setup view as a header within a card. The card provides information about installing Outline on an arbitrary cloud server provider." + }, + "setup_create": { + "message": "Create server", + "description": "This string appears in the server setup view as a button. Clicking it starts the server creation flow." + }, + "setup_description": { + "message": "Don't have a server? Create an account with DigitalOcean.", + "description": "This string appears in the server setup view as a sub-header. Prompts the user to create a DigitalOcean account to deploy Outline servers. DigitalOcean is a cloud provider name and should not be translated." + }, + "setup_do_cancel": { + "message": "Cancel at anytime", + "description": "This string appears in the server setup view as an item of a list describing Outline's features." + }, + "setup_do_cost": { + "message": "Only US$5 a month", + "description": "This string appears in the server setup view as an item of a list describing Outline's features." + }, + "setup_do_create": { + "message": "Create a new server with your DigitalOcean account for an additional US$5/30 days for 1 TB of data transfer.", + "description": "This string appears in the server setup view as a paragraph within a card. Describes Outline servers' features when deployed on DigitalOcean. DigitalOcean is a cloud provider name and should not be translated; TB is an abbreviation for terabyte and should not be translated" + }, + "setup_do_data": { + "message": "1 TB data transfer allowance", + "description": "This string appears in the server setup view as an item of a list describing Outline's features. Refers to the monthly amount of transfer data offered by a cloud server provider. TB is an abbreviation for terabyte and should not be translated." + }, + "setup_do_description": { + "message": "This could take up to two minutes. You can destroy this server at anytime.", + "description": "This string appears in the server setup view as a sub-header. Displayed when a server is being created along a progress bar." + }, + "setup_do_easiest": { + "message": "Easiest setup process", + "description": "This string appears in the server setup view as an item of a list describing Outline's features." + }, + "setup_do_title": { + "message": "Setting up Outline.", + "description": "This string appears in the server setup view as a header. Displayed when a server is being created along a progress bar." + }, + "setup_firewall_instructions": { + "message": "Firewall instructions", + "description": "This string appears in the server setup view as the header of a section that provides instructions to configure the server's firewall." + }, + "setup_recommended": { + "message": "Recommended", + "description": "This string appears in the server setup view as a label. Describes Outline servers deployment on DigitalOcean, which is easier from a user perspective." + }, + "setup_simple_commands": { + "message": "Simple install commands", + "description": "This string appears in the server setup view as an item of a list describing Outline's features." + }, + "setup_step-by-step": { + "message": "Step-by-step set-up guide", + "description": "This string appears in the server setup view as an item of a list describing Outline's features." + }, + "setup_tested": { + "message": "Tested on VULTR, Linode, and Liquid Web", + "description": "This string appears in the server setup view as an item of a list describing Outline's features. VULTR, Linode, and Liquid Web a cloud provider names and should not be translated." + }, + "setup_title": { + "message": "Choose a cloud service to set up Outline.", + "description": "This string appears in the server setup view as a header. Prompts the user to create deploy Outline servers on the cloud provider of their choice. 'Set up' means install and run the Outline server software." + }, + "share_description": { + "message": "Copy this invitation and send it from a communication tool you trust. $START_OF_LINK$Need help?$END_OF_LINK$", + "description": "This string appears as a sub-header of a dialog that provides instructions on how to share access to a server.", + "placeholders": { + "END_OF_LINK": { + "content": "{closeLink}" + }, + "START_OF_LINK": { + "content": "{openLink}" + } + } + }, + "share_invite": { + "message": "You\u2019re invited to connect to my Outline server. Use it to access the open internet, no matter where you are. Follow the instructions on your invitation link below to download the Outline App and get connected.", + "description": "This string appears as a paragraph within a dialog that provides an invitation to share access to a server. Belongs to the instructions for the user with whom access is being shared." + }, + "share_invite_copied": { + "message": "Copied to clipboard", + "description": "This string appears as a label within a dialog that provides an invitation to share access to a server. Displayed when the user clicks a button to copy the invitation text to the clipboard." + }, + "share_invite_copy": { + "message": "Copy invitation", + "description": "This string appears as a button within a dialog that provides an invitation to share access to a server. Clicking it copies the invitation instructions text to the clipboard." + }, + "share_invite_copy_access_key": { + "message": "Copy your access key:", + "description": "This string appears as a paragraph within a dialog that provides an invitation to share access to a server. Belongs to the instructions for the user with whom access is being shared." + }, + "share_invite_instructions": { + "message": "Follow our invitation instructions on GitHub:", + "description": "This string appears as a list item within a dialog that provides an invitation to share access to a server. Belongs to the instructions for the user with whom access is being shared." + }, + "share_invite_trouble": { + "message": "Having trouble accessing the invitation link?", + "description": "This string appears as a paragraph within a dialog that provides an invitation to share access to a server. Belongs to the instructions for the user with whom access is being shared." + }, + "share_title": { + "message": "Share access", + "description": "This string appears as the header of a dialog that provides an invitation to share access to a server." + }, + "terms_of_service": { + "message": "I have read and understood the $START_OF_LINK$Outline Terms of Service$END_OF_LINK$", + "description": "This string appears within an overlay the first time the user runs the app. Prompts the user to review and accept Outline's terms of service.", + "placeholders": { + "END_OF_LINK": { + "content": "{closeLink}" + }, + "START_OF_LINK": { + "content": "{openLink}" + } + } + } +} diff --git a/src/server_manager/model/server.ts b/src/server_manager/model/server.ts index c636e434..634f0356 100644 --- a/src/server_manager/model/server.ts +++ b/src/server_manager/model/server.ts @@ -120,7 +120,7 @@ export interface ManagedServerRepository { getRegionMap(): Promise>; // Creates a server and returning it when it becomes active (i.e. the server has // created, not necessarily once shadowbox installation has finished). - createServer(region: RegionId): Promise; + createServer(region: RegionId, name: string): Promise; } // Configuration for manual servers. This is the output emitted from the diff --git a/src/server_manager/ui_components/app-root.html b/src/server_manager/ui_components/app-root.html index df288c62..a3f099aa 100644 --- a/src/server_manager/ui_components/app-root.html +++ b/src/server_manager/ui_components/app-root.html @@ -16,6 +16,7 @@ + @@ -288,7 +289,7 @@ } - +
@@ -304,13 +305,13 @@
- DigitalOcean servers + [[localize('servers-digitalocean')]]
-

Disconnect DigitalOcean account

+

[[localize('digitalocean-disconnect-account')]]

-
Disconnect
+
[[localize('digitalocean-disconnect')]]
@@ -326,7 +327,7 @@
- Servers + [[localize('servers-manual')]]
@@ -80,6 +79,7 @@ Polymer({ is: 'outline-about-dialog', properties: { + localize: {type: Function, readonly: true}, outlineVersion: String }, open: function () { diff --git a/src/server_manager/ui_components/outline-cloud-instructions-view.html b/src/server_manager/ui_components/outline-cloud-instructions-view.html index fc69d9e2..ef860a2a 100644 --- a/src/server_manager/ui_components/outline-cloud-instructions-view.html +++ b/src/server_manager/ui_components/outline-cloud-instructions-view.html @@ -75,7 +75,7 @@
[[title]]
-
Show me where
+
[[localize('manual-server-show-me')]]
@@ -93,7 +93,8 @@ title: String, imagePath: String, thumbnailPath: String, - instructions: Array + instructions: Array, + localize: {type: Function, readonly: true}, }, _openImage: function() { this.fire('OpenImageRequested', {imagePath: this.imagePath}); diff --git a/src/server_manager/ui_components/outline-do-oauth-step.html b/src/server_manager/ui_components/outline-do-oauth-step.html index eea1cc64..83fe4721 100644 --- a/src/server_manager/ui_components/outline-do-oauth-step.html +++ b/src/server_manager/ui_components/outline-do-oauth-step.html @@ -71,50 +71,50 @@ - Sign in or create an account with DigitalOcean. - With your account, Outline makes it easy to create a server and get connected. + [[localize('oauth-connect-title')]] + [[localize('oauth-connect-description')]]
-

Waiting to connect your account...

+

[[localize('oauth-connect-tag')]]

- Cancel + [[localize('cancel')]]
- Activate you DigitalOcean account. - Check your inbox for an email from DigitalOcean, and click the link in it to confirm your account. + [[localize('oauth-activate-account')]] + [[localize('oauth-verify')]]
-

Confirm your email...

+

[[localize('oauth-verify-tag')]]

- Sign Out + [[localize('oauth-sign-out')]]
- Activate you DigitalOcean account. - Enter your billing information on digitalocean.com and return to the app once you are done. + [[localize('oauth-activate-account')]] + [[localize('oauth-billing')]]
-

Enter billing information...

+

[[localize('oauth-billing-tag')]]

- Sign Out + [[localize('oauth-sign-out')]]
- Activate you DigitalOcean account. - Your DigitalOcean account has been activated. + [[localize('oauth-activate-account')]] + [[localize('oauth-account-active')]]
-

Account activated! Loading server locations...

+

[[localize('oauth-account-active-tag')]]

- Sign Out + [[localize('oauth-sign-out')]]
@@ -132,6 +132,10 @@ type: String, value: 'Cancel' }, + localize: { + type: Function, + readonly: true + }, onCancel: Function }, _cancelTapped: function() { diff --git a/src/server_manager/ui_components/outline-feedback-dialog.html b/src/server_manager/ui_components/outline-feedback-dialog.html index f688dc6a..c1a6417d 100644 --- a/src/server_manager/ui_components/outline-feedback-dialog.html +++ b/src/server_manager/ui_components/outline-feedback-dialog.html @@ -88,34 +88,31 @@ see https://github.com/PolymerElements/paper-swatch-picker/issues/36 -->

[[feedbackExplanation]]

- General feedback - Having trouble installing Outline - Can't connect to my server - Others can't connect to my server - Having trouble managing my server - Suggestions + [[localize('feedback-general')]] + [[localize('feedback-install')]] + [[localize('feedback-connection')]] + [[localize('feedback-connection-others')]] + [[localize('feedback-management')]] + [[localize('feedback-suggestion')]] - + DigitalOcean Amazon Web Services Google Cloud Platform - Other + [[localize('feedback-other')]] - -

Please note that our team is only able to answer feedback in English.

- -

- Your feedback, email address (if provided) and additional information - referred to in the privacy policy - will be sent to the Outline team. + +

[[localize('feedback-disclaimer')]]

+ +

- Cancel - Submit + [[localize('cancel')]] + [[localize('feedback-submit')]]

@@ -149,19 +146,25 @@ see https://github.com/PolymerElements/paper-swatch-picker/issues/36 --> type: Boolean, computed: '_computeShouldShowLanguageDisclaimer(hasEnteredEmail)', }, + localize: { + type: Function, + readonly: true + }, }, - open: function(prepopulatedMessage, showInstallationFailed, feedbackCategory = 0) { + open: function(prepopulatedMessage, showInstallationFailed) { // Clear all fields, in case feedback had already been entered. if (showInstallationFailed) { - this.title = 'Outline Server Installation Failed'; - this.feedbackExplanation = 'An error occurred while attempting to install Outline on your server. If you haven\'t been able to figure out a solution, please consider sending us feedback and telling us your email address (optional) so that we can get back to you.'; + this.title = this.localize('feedback-title-install'); + this.feedbackExplanation = this.localize('feedback-explanation-install'); this.$.dialog.classList.add('installationFailed'); this.$.feedbackCategoryListbox.selected = this.feedbackCategories.INSTALLATION; } else { - this.title = 'Send Feedback'; + this.title = this.localize('feedback-title-generic'); this.feedbackExplanation = ''; this.$.dialog.classList.remove('installationFailed'); - this.$.feedbackCategoryListbox.selected = feedbackCategory; + // TODO(alalama): figure out why the localized category doesn't get displayed the first + // time opening the dialog. + this.$.feedbackCategoryListbox.selected = this.feedbackCategories.GENERAL; } this.$.userFeedback.invalid = false; this.$.userFeedback.value = prepopulatedMessage || ''; diff --git a/src/server_manager/ui_components/outline-intro-step.html b/src/server_manager/ui_components/outline-intro-step.html index 01a35932..e13705d3 100644 --- a/src/server_manager/ui_components/outline-intro-step.html +++ b/src/server_manager/ui_components/outline-intro-step.html @@ -89,6 +89,7 @@ font-weight: 500; letter-spacing: .05em; font-size: 10px; + text-transform: uppercase; } .card#digital-ocean .tag, .card#digital-ocean .email, .card p { color: var(--medium-gray); @@ -155,13 +156,13 @@ - Choose a cloud service, set up Outline. - Don't have a server? Create an account with DigitalOcean. + [[localize('setup-title')]] + [[localize('setup-description')]]
-
RECOMMENDED
+
[[localize('setup-recommended')]]
@@ -169,78 +170,78 @@
    -
  • Easiest setup process
  • -
  • Only $5 a month
  • -
  • 1 TB of data transfer
  • -
  • Cancel at anytime
  • +
  • [[localize('setup-do-easiest')]]
  • +
  • [[localize('setup-do-cost')]]
  • +
  • [[localize('setup-do-data')]]
  • +
  • [[localize('setup-do-cancel')]]

- Create a new server with your DigitalOcean account for an additional $5 USD/30 days for 1TB of data transfer. + [[localize('setup-do-create')]]

-
ADVANCED
+
[[localize('setup-advanced')]]
Google Cloud Platform
    -
  • Step-by-step set-up guide
  • -
  • Firewall instructions
  • -
  • Simple install commands
  • +
  • [[localize('setup-step-by-step')]]
  • +
  • [[localize('setup-firewall-instructions')]]
  • +
  • [[localize('setup-simple-commands')]]
-
ADVANCED
+
[[localize('setup-advanced')]]
Amazon Web Services
    -
  • Step-by-step set-up guide
  • -
  • EC2 firewall instructions
  • -
  • Simple install commands
  • +
  • [[localize('setup-step-by-step')]]
  • +
  • [[localize('setup-firewall-instructions')]]
  • +
  • [[localize('setup-simple-commands')]]
-
ADVANCED
+
[[localize('setup-advanced')]]
-
Set up Outline anywhere
+
[[localize('setup-anywhere')]]
    -
  • Tested on VULTR, Linode, and Liquid Web
  • -
  • Simple install commands
  • +
  • [[localize('setup-tested')]]
  • +
  • [[localize('setup-simple-commands')]]
@@ -254,6 +255,10 @@ properties: { isSignedInToDigitalOcean: Boolean, digitalOceanEmail: String, + localize: { + type: Function, + readonly: true, + }, }, connectToDigitalOceanTapped: function() { this.fire('ConnectToDigitalOcean'); diff --git a/src/server_manager/ui_components/outline-manual-server-entry.html b/src/server_manager/ui_components/outline-manual-server-entry.html index 058c8ef3..8b4c20f7 100644 --- a/src/server_manager/ui_components/outline-manual-server-entry.html +++ b/src/server_manager/ui_components/outline-manual-server-entry.html @@ -162,42 +162,43 @@ } - Follow the instructions below - These steps will help you install Outline on a [[cloudProviderName]] Linux server. + [[localize('manual-server-title')]] + [[localize('manual-server-description', 'cloudProvider', cloudProviderName)]]
+ 1 -
Configure your firewall
+
[[localize('manual-server-firewall')]]
- +
    -
  1. Add a new firewall rule to your Compute Engine project.
  2. -
  3. Type 'outline' in the 'Name' field.
  4. -
  5. Type 'outline' in the 'Target tags' field.
  6. -
  7. Type '0.0.0.0/0' in the 'Source IP ranges' field.
  8. -
  9. Select 'Allow all' under 'Protocols and ports'.
  10. -
  11. Click 'Create'.
  12. +
  13. +
  14. [[localize('gcp-firewall-create-1')]]
  15. +
  16. [[localize('gcp-firewall-create-2')]]
  17. +
  18. [[localize('gcp-firewall-create-3')]]
  19. +
  20. [[localize('gcp-firewall-create-4')]]
  21. +
  22. [[localize('gcp-firewall-create-5')]]
- +
    -
  1. Navigate to the VM instances screen.
  2. -
  3. Click on the instance where you want to host Outline.
  4. -
  5. Click 'EDIT' at the top of the VM Instance details screen.
  6. -
  7. Type 'outline' in the 'Network tags' field.
  8. -
  9. Click 'SAVE'.
  10. +
  11. +
  12. [[localize('gcp-firewall-assign-1')]]
  13. +
  14. [[localize('gcp-firewall-assign-2')]]
  15. +
  16. [[localize('gcp-firewall-assign-3')]]
  17. +
  18. [[localize('gcp-firewall-assign-4')]]
@@ -207,33 +208,33 @@
1 -
Configure your firewall
+
[[localize('manual-server-firewall')]]
- +
    -
  1. Once you've created your EC2 instance, open the EC2 Management Console.
  2. -
  3. Under 'Network & Security', select 'Security Groups'.
  4. -
  5. Click on 'Create Security Group'.
  6. -
  7. Type 'outline' in the 'Security group name' and 'Description' fields.
  8. -
  9. Click 'Add Rule'.
  10. -
  11. Select 'All traffic' under 'Type', and 'Anywhere' under 'Source'.
  12. +
  13. +
  14. [[localize('aws-group-create-1')]]
  15. +
  16. [[localize('aws-group-create-2')]]
  17. +
  18. [[localize('aws-group-create-3')]]
  19. +
  20. [[localize('aws-group-create-4')]]
  21. +
  22. [[localize('aws-group-create-5')]]
- +
    -
  1. Navigate to Instances.
  2. -
  3. Click 'Actions' > 'Networking' > 'Change Security Groups' at the top of the 'Instances' screen.
  4. -
  5. Select the 'outline' Security Group, unselect other security groups.
  6. -
  7. Click 'Assign Security Groups'.
  8. +
  9. +
  10. [[localize('aws-group-assign-1')]]
  11. +
  12. [[localize('aws-group-assign-2')]]
  13. +
  14. [[localize('aws-group-assign-3')]]
@@ -244,7 +245,7 @@
1 2 -
Log into your server, and run this command.
+
[[localize('manual-server-install-run')]]
sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh)"
@@ -255,15 +256,15 @@
2 3 -
Paste your installation output here.
+
[[localize('manual-server-install-paste')]]
- CANCEL - DONE + [[localize('cancel')]] + [[localize('done')]]
@@ -302,7 +303,11 @@ enableDoneButton: { type: Boolean, value: false, - } + }, + localize: { + type: Function, + readonly: true, + }, }, doneTapped: function() { this.showConnection = true; diff --git a/src/server_manager/ui_components/outline-metrics-option-dialog.html b/src/server_manager/ui_components/outline-metrics-option-dialog.html index da9a26e6..67b1e08a 100644 --- a/src/server_manager/ui_components/outline-metrics-option-dialog.html +++ b/src/server_manager/ui_components/outline-metrics-option-dialog.html @@ -22,24 +22,30 @@ diff --git a/src/server_manager/ui_components/outline-server-progress-step.html b/src/server_manager/ui_components/outline-server-progress-step.html index 3a9b1c4f..ed2c8779 100644 --- a/src/server_manager/ui_components/outline-server-progress-step.html +++ b/src/server_manager/ui_components/outline-server-progress-step.html @@ -60,10 +60,10 @@ - Setting up Outline. - This could take up to two minutes. You can destroy this server at anytime. + [[localize('setup-do-title')]] + [[localize('setup-do-description')]] - Cancel + [[localize('cancel')]]
@@ -82,7 +82,8 @@ properties: { serverName: String, showCancelButton: Boolean, - updateIntervalId: Number + updateIntervalId: Number, + localize: Function }, start: function() { if (this.updateIntervalId) { diff --git a/src/server_manager/ui_components/outline-server-settings.html b/src/server_manager/ui_components/outline-server-settings.html index fb98cdb0..f75c83ba 100644 --- a/src/server_manager/ui_components/outline-server-settings.html +++ b/src/server_manager/ui_components/outline-server-settings.html @@ -120,23 +120,23 @@
-

Digital Ocean

- - - +

DigitalOcean

+ + +
-

Server Information

- -

Set a new name for your server. Note that this will not be reflected on the devices of the users that you invited to connect to it.

- - - - - +

[[localize('settings-server-info')]]

+ +

[[localize('settings-server-rename')]]

+ + + + +
@@ -144,10 +144,10 @@
-

Share anonymous metrics

+

[[localize('settings-metrics-header')]]

-

Share anonymized metrics to help improve the reliability and performance of Outline, for you and for those you share your server with. Learn more.

+

@@ -169,6 +169,7 @@ serverLocation: {type: String, value: null}, serverMonthlyCost: {type: String, value: null}, serverMonthlyTransferLimit: {type: String, value: null}, + localize: {type: Function, readonly: true}, }, update: function(name, metricsEnabled) { this.initialName = name; diff --git a/src/server_manager/ui_components/outline-server-view.html b/src/server_manager/ui_components/outline-server-view.html index 725cb435..15157166 100644 --- a/src/server_manager/ui_components/outline-server-view.html +++ b/src/server_manager/ui_components/outline-server-view.html @@ -341,10 +341,10 @@ - Destroy server + [[localize('server-destroy')]] - Remove server + [[localize('server-remove')]] @@ -354,21 +354,24 @@
- Connections - Settings + [[localize('server-connections')]] + [[localize('server-settings')]]
-

Server unreachable

-

We're having issues connecting to this server. Try again or remove this server from the application.

-

We're having issues connecting to this server. Try again or destroy this server and the virtual host.

+

[[localize('server-unreachable')]]

+

+

[[localize('server-unreachable-description')]]
+ [[localize('server-unreachable-managed-description')]] + [[localize('server-unreachable-manual-description')]] +

- Remove server - Destroy server - Try again + [[localize('server-remove')]] + [[localize('server-destroy')]] + [[localize('retry')]]
@@ -381,7 +384,7 @@

[[_getFormattedTransferredValue(totalInboundBytes, '0')]]

[[_getFormattedTransferredUnit(totalInboundBytes, 'B')]]

-

Data transferred / 30 days

+

[[localize('server-data-transfer')]]

@@ -389,17 +392,17 @@

[[managedServerUtilzationPercentage]]

-

of [[_formatBytesTransferred(monthlyOutboundTransferBytes)]]

+

/[[_formatBytesTransferred(monthlyOutboundTransferBytes)]]

-

Data used / 30 days

+

[[localize('server-data-used')]]

[[accessKeyRows.length]]

-

Keys

+

[[localize('server-keys')]]

-

Server access

+

[[localize('server-access')]]

@@ -407,12 +410,12 @@
- Access keys + [[localize('server-access-keys')]] - Usage / 30 days + [[localize('server-usage')]] @@ -423,8 +426,8 @@
-
My access key
-
Connect your devices
+
[[localize('server-my-access-key')]]
+
[[localize('server-connect-devices')]]
@@ -465,10 +468,10 @@ - Rename + [[localize('server-access-key-rename')]] - Remove + [[localize('remove')]] @@ -481,7 +484,7 @@
-
Add new key
+
[[localize('server-access-key-new')]]
@@ -497,23 +500,24 @@ server-monthly-cost="[[monthlyCost]]" server-monthly-transfer-limit="[[_formatBytesTransferred(monthlyOutboundTransferBytes)]]" is-server-managed="[[isServerManaged]]" - server-location="[[serverLocation]]"> + server-location="[[serverLocation]]" + localize="[[localize]]">
- + -

You are not connected yet!

-

Click here to install the Outline client app, using your personal access key to your Outline server

- Okay, got it! +

[[localize('server-help-connection-title')]]

+

[[localize('server-help-connection-description')]]

+ [[localize('server-help-connection-ok')]]
- + -

Create keys, share access

-

Share access keys with friends, so they can connect to your Outline server. They can use the same access key on all their devices

- Next +

[[localize('server-help-access-key-title')]]

+

[[localize('server-help-access-key-description')]]

+ [[localize('server-help-access-key-next')]]
@@ -570,6 +574,10 @@ type: Number, value: 1 }, + localize: { + type: Function, + readonly: true + }, }, observers: [ '_accessKeysAddedOrRemoved(accessKeyRows.splices)', @@ -751,10 +759,10 @@ // are initialized before this point, setPosition will not work and // they will appear in the top left of the view. initHelpBubbles: function() { - this.showHelpBubbleOnce('addAccessKeyHelpBubble', 'addAccessKeyRow', 'up', 'left'); + this.showHelpBubbleOnce('addAccessKeyHelpBubble', 'addAccessKeyRow', 'down', 'left'); }, showHelpBubbleOnce: function(helpBubbleId, positionTargetId, - arrowDirection='up', arrowAlignment='right') { + arrowDirection='down', arrowAlignment='right') { const storageKey = helpBubbleId + '-dismissed'; if (window.localStorage.getItem(storageKey)) { // Help bubble has already been dismissed, nothing to show. diff --git a/src/server_manager/ui_components/outline-share-dialog.html b/src/server_manager/ui_components/outline-share-dialog.html index 3b81fc0e..15491d87 100644 --- a/src/server_manager/ui_components/outline-share-dialog.html +++ b/src/server_manager/ui_components/outline-share-dialog.html @@ -95,38 +95,41 @@
-

Share access

-

- Copy this invitation and send it from a communication tool you trust. Need help? -

+

[[localize('share-title')]]

+

-

You’re invited to connect to my Outline server. Use it to access the open internet, no matter where you are. Follow the instructions on your invitation link below to download the Outline App and get connected.

+

[[localize('share-invite')]]

{{s3Url}}

-----

-

Having trouble accessing the invitation link?

+

[[localize('share-invite-trouble')]]

  1. - Copy your access key: + [[localize('share-invite-copy-access-key')]] {{acessKey}}
  2. - Follow our invitation instructions on GitHub: + [[localize('share-invite-instructions')]] https://github.com/Jigsaw-Code/outline-client/blob/master/docs/invitation-instructions.md
- Copy invitation - Done + [[localize('share-invite-copy')]] + [[localize('done')]]
- +
diff --git a/src/server_manager/web_app/app.spec.ts b/src/server_manager/web_app/app.spec.ts index 00963b2f..7c12cbd2 100644 --- a/src/server_manager/web_app/app.spec.ts +++ b/src/server_manager/web_app/app.spec.ts @@ -318,6 +318,7 @@ class FakePolymerAppRoot { // and throwing an exception would result in breakage. setAttribute() {} addEventListener() {} + localize() {} } class FakeServer implements server.Server { diff --git a/src/server_manager/web_app/app.ts b/src/server_manager/web_app/app.ts index 0c5a73a8..f2f3faea 100644 --- a/src/server_manager/web_app/app.ts +++ b/src/server_manager/web_app/app.ts @@ -45,23 +45,6 @@ interface UiAccessKey { relativeTraffic: number; } -// Converts the access key from the remote service format to the -// format used by outline-server-view. -function convertToUiAccessKey(remoteAccessKey: server.AccessKey): UiAccessKey { - return { - id: remoteAccessKey.id, - placeholderName: 'Key ' + remoteAccessKey.id, - name: remoteAccessKey.name, - accessUrl: remoteAccessKey.accessUrl, - transferredBytes: 0, - relativeTraffic: 0 - }; -} - -const DIGITAL_OCEAN_CREATION_ERROR_MESSAGE = `Sorry! We couldn't create a server this time. - If this problem persists, it might be that your account needs to be reviewed by DigitalOcean. - Please log in to www.digitalocean.com and follow their instructions.`; - function isManagedServer(testServer: server.Server): testServer is server.ManagedServer { return !!(testServer as server.ManagedServer).getHost; } @@ -145,13 +128,15 @@ export class App { manualServerEntryEl.clear(); }) .catch((e: Error) => { - // Remove the "Attempting to connect..." display. + // Remove the progress indicator. manualServerEntryEl.showConnection = false; // Display either error dialog or feedback depending on error type. if (e instanceof errors.UnreachableServerError) { - const errorTitle = 'Unable to connect to your Outline Server'; - this.appRoot.showManualServerError(errorTitle, e.message); + const errorTitle = appRoot.localize('error-server-unreachable-title'); + const errorMessage = appRoot.localize('error-server-unreachable'); + this.appRoot.showManualServerError(errorTitle, errorMessage); } else { + // TODO(alalama): with UI validation, this code path never gets executed. Remove? let errorMessage = ''; if (e.message) { errorMessage += `${e.message}\n`; @@ -180,9 +165,10 @@ export class App { user: {email: detail.userEmail}, tags: {category: detail.feedbackCategory, cloudProvider: detail.cloudProvider} }); - appRoot.showNotification('Thanks for helping us improve! We love hearing from you.'); + appRoot.showNotification(appRoot.localize('notification-feedback-thanks')); } catch (e) { - appRoot.showError('Failed to submit feedback. Please try again.'); + console.error(`Failed to submit feedback: ${e}`); + appRoot.showError(appRoot.localize('error-feedback')); } }); @@ -253,8 +239,14 @@ export class App { this.displayServerRepository.removeServer(displayServer); } const unsyncedServerNames = unsyncedServers.map(s => s.name).join(', '); - this.appRoot.showError( - `${unsyncedServerNames} no longer present in your DigitalOcean account.`); + let messageKey = 'error-server-removed'; + let placeholder = 'serverName'; + if (unsyncedServers.length > 1) { + // Pluralize localized message. + messageKey = 'error-servers-removed'; + placeholder = 'serverNames'; + } + this.appRoot.showError(this.appRoot.localize(messageKey, placeholder, unsyncedServerNames)); } await this.syncDisplayServersToUi(); @@ -344,7 +336,7 @@ export class App { // Set name to the default server name for this region. Because the server // is still being created, the getName REST API will not yet be available. const regionId = this.serverBeingCreated.getHost().getRegionId(); - const serverName = digitalocean_server.MakeEnglishNameForServer(regionId); + const serverName = this.makeLocalizedServerName(regionId); return { // Use the droplet ID until the API URL is available. id: this.serverBeingCreated.getHost().getHostId(), @@ -429,9 +421,10 @@ export class App { .catch((error) => { if (!cancelled) { this.showIntro(); - const msg = 'Failed to get DigitalOcean account information'; - this.displayError(msg, error); - reject(new Error(`${msg}: ${error}`)); + const msg = `Failed to get DigitalOcean account information: ${error}`; + console.error(msg); + this.appRoot.showError(this.appRoot.localize('error-do-account-info')); + reject(new Error(msg)); } }); }; @@ -508,7 +501,7 @@ export class App { } else { // Server has been deleted outside the app. this.appRoot.showError( - `${displayServer.name} no longer present in your DigitalOcean account.`); + this.appRoot.localize('error-server-removed', 'serverName', displayServer.name)); this.removeServerFromDisplay(displayServer); this.selectedServer = null; this.appRoot.selectedServer = null; @@ -554,25 +547,13 @@ export class App { }); }; - private displayError(message: string, cause: Error) { - console.error(`${message}: ${cause}`); - this.appRoot.showError(message); - console.error(message); - } - - private displayNotification(message: string) { - this.appRoot.showNotification(message); - } - // Shows the intro screen with overview and options to sign in or sign up. private showIntro() { this.appRoot.showIntro(); } private displayAppUpdateNotification() { - const msg = - 'An updated version of the Outline Manager has been downloaded. It will be installed when you restart the application.'; - this.appRoot.showToast(msg, 60000); + this.appRoot.showNotification(this.appRoot.localize('notification-app-update'), 60000); } private connectToDigitalOcean() { @@ -623,7 +604,8 @@ export class App { if (!session.isCancelled()) { this.clearCredentialsAndShowIntro(); bringToFront(); - this.displayError('Authentication with DigitalOcean failed', error); + console.error(`DigitalOcean authentication failed: ${error}`); + this.appRoot.showError(this.appRoot.localize('error-do-auth')); } }); } @@ -671,7 +653,8 @@ export class App { regionPicker.availableRegionIds = availableRegionIds; }, (e) => { - this.displayError('Failed to get list of available regions', e); + console.error(`Failed to get list of available regions: ${e}`); + this.appRoot.showError(this.appRoot.localize('error-do-regions')); }); } @@ -700,13 +683,15 @@ export class App { this.serverBeingCreated = null; return; } - const errorMessage = this.serverBeingCreated.isInstallCompleted() ? - 'We are unable to connect to your Outline server at the moment. This may be due to a firewall on your network or temporary connectivity issues with digitalocean.com.' : - 'There was an error creating your Outline server. This may be due to a firewall on your network or temporary connectivity issues with digitalocean.com.'; + let errorMessage = this.serverBeingCreated.isInstallCompleted() ? + this.appRoot.localize('error-server-unreachable-title') : + this.appRoot.localize('error-server-creation'); + errorMessage += ` ${this.appRoot.localize('digitalocean-unreachable')}`; this.appRoot .showModalDialog( null, // Don't display any title. - errorMessage, ['Delete this server', 'Try again']) + errorMessage, + [this.appRoot.localize('server-destroy'), this.appRoot.localize('retry')]) .then((clickedButtonIndex: number) => { if (clickedButtonIndex === 0) { // user clicked 'Delete this server' console.info('Deleting unreachable server'); @@ -722,12 +707,23 @@ export class App { }); } + private getLocalizedCityName(regionId: server.RegionId) { + const cityId = digitalocean_server.GetCityId(regionId); + return this.appRoot.localize(`city-${cityId}`); + } + + private makeLocalizedServerName(regionId: server.RegionId) { + const serverLocation = this.getLocalizedCityName(regionId); + return this.appRoot.localize('server-name', 'serverLocation', serverLocation); + } + // Returns a promise which fulfills once the DigitalOcean droplet is created. // Shadowbox may not be fully installed once this promise is fulfilled. public createDigitalOceanServer(regionId: server.RegionId) { + const serverName = this.makeLocalizedServerName(regionId); return this .digitalOceanRetry(() => { - return this.digitalOceanRepository.createServer(regionId); + return this.digitalOceanRepository.createServer(regionId, serverName); }) .then((server) => { this.syncServerCreationToUi(server); @@ -761,6 +757,7 @@ export class App { view.serverHostname = selectedServer.getHostname(); view.serverManagementApiUrl = selectedServer.getManagementApiUrl(); view.serverPortForNewAccessKeys = selectedServer.getPortForNewAccessKeys(); + // TODO(alalama): use actual locale. view.serverCreationDate = selectedServer.getCreatedDate().toLocaleString( 'en-US', {year: 'numeric', month: 'long', day: 'numeric'}); @@ -770,7 +767,7 @@ export class App { view.monthlyCost = host.getMonthlyCost().usd; view.monthlyOutboundTransferBytes = host.getMonthlyOutboundTransferLimit().terabytes * (2 ** 40); - view.serverLocation = digitalocean_server.GetEnglishCityName(host.getRegionId()); + view.serverLocation = this.getLocalizedCityName(host.getRegionId()); } else { view.isServerManaged = false; } @@ -783,14 +780,15 @@ export class App { // Load "My Connection" and other access keys. selectedServer.listAccessKeys() .then((serverAccessKeys: server.AccessKey[]) => { - view.accessKeyRows = serverAccessKeys.map(convertToUiAccessKey); + view.accessKeyRows = serverAccessKeys.map(this.convertToUiAccessKey.bind(this)); // Initialize help bubbles once the page has rendered. setTimeout(() => { view.initHelpBubbles(); }, 250); }) .catch((error) => { - this.displayError('Could not load keys', error); + console.error(`Failed to load access keys: ${error}`); + this.appRoot.showError(this.appRoot.localize('error-keys-get')); }); this.showTransferStats(selectedServer, view); @@ -870,20 +868,36 @@ export class App { } private getS3InviteUrl(accessUrl: string, isAdmin = false) { + // TODO(alalama): display the invite in the user's preferred language. const adminParam = isAdmin ? '?admin_embed' : ''; return `https://s3.amazonaws.com/outline-vpn/invite.html${adminParam}#${ encodeURIComponent(accessUrl)}`; } + // Converts the access key from the remote service format to the + // format used by outline-server-view. + private convertToUiAccessKey(remoteAccessKey: server.AccessKey): UiAccessKey { + return { + id: remoteAccessKey.id, + placeholderName: `${this.appRoot.localize('key')} ${remoteAccessKey.id}`, + name: remoteAccessKey.name, + accessUrl: remoteAccessKey.accessUrl, + transferredBytes: 0, + relativeTraffic: 0 + }; + } + + private addAccessKey() { this.selectedServer.addAccessKey() .then((serverAccessKey: server.AccessKey) => { - const uiAccessKey = convertToUiAccessKey(serverAccessKey); + const uiAccessKey = this.convertToUiAccessKey(serverAccessKey); this.appRoot.getServerView(this.appRoot.selectedServer.id).addAccessKey(uiAccessKey); - this.displayNotification('Key added'); + this.appRoot.showNotification(this.appRoot.localize('notification-key-added')); }) .catch((error) => { - this.displayError('Failed to add key', error); + console.error(`Failed to add access key: ${error}`); + this.appRoot.showError(this.appRoot.localize('error-key-add')); }); } @@ -893,7 +907,8 @@ export class App { entry.commitName(); }) .catch((error) => { - this.displayError('Failed to rename key', error); + console.error(`Failed to rename access key: ${error}`); + this.appRoot.showError(this.appRoot.localize('error-key-rename')); entry.revertName(); }); } @@ -906,7 +921,9 @@ export class App { serverConfig = parseManualServerConfig(userInput); } catch (e) { // This shouldn't happen because the UI validates the URL before enabling the DONE button. - return Promise.reject(new Error(`could not parse server config: ${e.message}`)); + const msg = `could not parse server config: ${e.message}`; + console.error(msg); + return Promise.reject(new Error(msg)); } // Don't let `ManualServerRepository.addServer` throw to avoid redundant error handling if we @@ -914,7 +931,7 @@ export class App { const storedServer = this.manualServerRepository.findServer(serverConfig); if (!!storedServer) { return this.syncServerToDisplay(storedServer).then((displayServer) => { - this.appRoot.showToast('Server already added', 5000); + this.appRoot.showNotification(this.appRoot.localize('notification-server-exists'), 5000); this.showServerIfHealthy(storedServer, displayServer); }); } @@ -926,8 +943,7 @@ export class App { // Remove inaccessible manual server from local storage if it was just created. manualServer.forget(); console.error('Manual server installed but unreachable.'); - return Promise.reject(new errors.UnreachableServerError( - 'Your Outline Server was installed correctly, but we are not able to connect to it. Most likely this is because your server\'s firewall rules are blocking incoming connections. Please review them and make sure to allow incoming TCP connections on ports ranging from 1024 to 65535.')); + return Promise.reject(new errors.UnreachableServerError()); } }); }); @@ -937,10 +953,11 @@ export class App { this.selectedServer.removeAccessKey(accessKeyId) .then(() => { this.appRoot.getServerView(this.appRoot.selectedServer.id).removeAccessKey(accessKeyId); - this.displayNotification('Key removed'); + this.appRoot.showNotification(this.appRoot.localize('notification-key-removed')); }) .catch((error) => { - this.displayError('Failed to remove key', error); + console.error(`Failed to remove access key: ${error}`); + this.appRoot.showError(this.appRoot.localize('error-key-remove')); }); } @@ -952,9 +969,9 @@ export class App { throw new Error(msg); } - const confirmationTitle = 'Destroy Server?'; - const confirmationText = 'Existing users will lose access. This action cannot be undone.'; - const confirmationButton = 'DESTROY'; + const confirmationTitle = this.appRoot.localize('confirmation-server-destroy-title'); + const confirmationText = this.appRoot.localize('confirmation-server-destroy'); + const confirmationButton = this.appRoot.localize('destroy'); this.appRoot.getConfirmation(confirmationTitle, confirmationText, confirmationButton, () => { this.digitalOceanRetry(() => { return serverToDelete.getHost().delete(); @@ -965,12 +982,14 @@ export class App { this.appRoot.selectedServer = null; this.selectedServer = null; this.showIntro(); - this.displayNotification('Server destroyed'); + this.appRoot.showNotification( + this.appRoot.localize('notification-server-destroyed')); }, (e) => { // Don't show a toast on the login screen. if (!(e instanceof digitalocean_api.XhrError)) { - this.displayError('Failed to destroy server', e); + console.error(`Failed destroy server: ${e}`); + this.appRoot.showError(this.appRoot.localize('error-server-destroy')); } }); }); @@ -984,17 +1003,16 @@ export class App { throw new Error(msg); } - const confirmationTitle = 'Remove Server?'; - const confirmationText = - 'This action removes your server from the Outline Manager, but does not block proxy access to users. You will still need to manually delete the Outline server from your host machine.'; - const confirmationButton = 'REMOVE'; + const confirmationTitle = this.appRoot.localize('confirmation-server-remove-title'); + const confirmationText = this.appRoot.localize('confirmation-server-remove'); + const confirmationButton = this.appRoot.localize('remove'); this.appRoot.getConfirmation(confirmationTitle, confirmationText, confirmationButton, () => { serverToForget.forget(); this.removeServerFromDisplay(this.appRoot.selectedServer); this.appRoot.selectedServer = null; this.selectedServer = null; this.showIntro(); - this.displayNotification('Server removed'); + this.appRoot.showNotification(this.appRoot.localize('notification-server-removed')); }); } @@ -1006,7 +1024,8 @@ export class App { metricsEnabled; }) .catch((error) => { - this.displayError('Error setting metrics enabled', error); + console.error(`Failed to set metrics enabled: ${error}`); + this.appRoot.showError(this.appRoot.localize('error-metrics')); }); } @@ -1017,7 +1036,8 @@ export class App { return this.syncAndShowServer(this.selectedServer); }) .catch((error) => { - this.displayError('Error renaming server', error); + console.error(`Failed to rename server: ${error}`); + this.appRoot.showError(this.appRoot.localize('error-server-rename')); }); } diff --git a/src/server_manager/web_app/build_action.sh b/src/server_manager/web_app/build_action.sh index ee81939d..90b14434 100755 --- a/src/server_manager/web_app/build_action.sh +++ b/src/server_manager/web_app/build_action.sh @@ -55,4 +55,4 @@ mkdir -p $STATIC_DIR cp -r $OUT_DIR/browserified/* $STATIC_DIR/ # Copy static resources -cp -r $ROOT_DIR/src/server_manager/{bower_components,ui_components,index.html,images} $STATIC_DIR +cp -r $ROOT_DIR/src/server_manager/{bower_components,ui_components,index.html,images,messages} $STATIC_DIR diff --git a/src/server_manager/web_app/digitalocean_server.ts b/src/server_manager/web_app/digitalocean_server.ts index 0b1bb329..a1d9ca87 100644 --- a/src/server_manager/web_app/digitalocean_server.ts +++ b/src/server_manager/web_app/digitalocean_server.ts @@ -52,27 +52,6 @@ function makeKeyValueTag(key: string, value: string) { return [KEY_VALUE_TAG, key, asciiToHex(value)].join(':'); } -const cityEnglishNameById: {[key: string]: string} = { - ams: 'Amsterdam', - sgp: 'Singapore', - blr: 'Bangalore', - fra: 'Frankfurt', - lon: 'London', - sfo: 'San Francisco', - tor: 'Toronto', - nyc: 'New York' -}; - -// Returns a name for a server in the given region. -export function MakeEnglishNameForServer(regionId: server.RegionId) { - return `Outline Server ${cityEnglishNameById[getCityId(regionId)]}`; -} - -// Returns the English name of the given region. -export function GetEnglishCityName(regionId: server.RegionId) { - return cityEnglishNameById[getCityId(regionId)]; -} - // Possible install states for DigitaloceanServer. enum InstallState { // Unknown state - server may still be installing. @@ -354,7 +333,7 @@ function startsWithCaseInsensitive(text: string, prefix: string) { return text.slice(0, prefix.length).toLowerCase() === prefix.toLowerCase(); } -function getCityId(slug: server.RegionId): string { +export function GetCityId(slug: server.RegionId): string { return slug.substr(0, 3).toLowerCase(); } @@ -372,7 +351,7 @@ export class DigitaloceanServerRepository implements server.ManagedServerReposit return this.digitalOcean.getRegionInfo().then((regions) => { const ret: server.RegionMap = {}; regions.forEach((region) => { - const cityId = getCityId(region.slug); + const cityId = GetCityId(region.slug); if (!(cityId in ret)) { ret[cityId] = []; } @@ -385,8 +364,7 @@ export class DigitaloceanServerRepository implements server.ManagedServerReposit } // Creates a server and returning it when it becomes active. - createServer(region: server.RegionId): Promise { - const name = MakeEnglishNameForServer(region); + createServer(region: server.RegionId, name: string): Promise { console.time('activeServer'); console.time('servingServer'); const onceKeyPair = crypto.generateKeyPair();