diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000000000000000000000000000000000000..da0415a0f5aad2f6109ef1e7ffdcdd1165577665 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8" /> +</project> \ No newline at end of file diff --git a/.idea/fileTemplates/code/I18nized Concatenation.java b/.idea/fileTemplates/code/I18nized Concatenation.java new file mode 100644 index 0000000000000000000000000000000000000000..57d92f8845fe0dcac49dd584c585e4747ffdf9e1 --- /dev/null +++ b/.idea/fileTemplates/code/I18nized Concatenation.java @@ -0,0 +1 @@ +i18nF("${PROPERTY_KEY}", ${PARAMETERS}) diff --git a/.idea/fileTemplates/code/I18nized Expression.java b/.idea/fileTemplates/code/I18nized Expression.java new file mode 100644 index 0000000000000000000000000000000000000000..d816cf765d099d0cc4c44cd98eb11181bbfd9573 --- /dev/null +++ b/.idea/fileTemplates/code/I18nized Expression.java @@ -0,0 +1 @@ +i18n("${PROPERTY_KEY}") \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000000000000000000000000000000000..741e3e4f965cf92ce8a910ce02316a71da9b87d2 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,17 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="HardCodedStringLiteral" enabled="true" level="WEAK WARNING" enabled_by_default="true"> + <option name="ignoreForAssertStatements" value="true" /> + <option name="ignoreForExceptionConstructors" value="true" /> + <option name="ignoreForSpecifiedExceptionConstructors" value="" /> + <option name="ignoreForJUnitAsserts" value="true" /> + <option name="ignoreForClassReferences" value="true" /> + <option name="ignoreForPropertyKeyReferences" value="true" /> + <option name="ignoreForNonAlpha" value="true" /> + <option name="ignoreAssignedToConstants" value="false" /> + <option name="ignoreToString" value="false" /> + <option name="nonNlsCommentPattern" value="NON-NLS" /> + </inspection_tool> + </profile> +</component> \ No newline at end of file diff --git a/.tx/config b/.tx/config new file mode 100644 index 0000000000000000000000000000000000000000..dff1b2b932beac7e24680c70b3af7d24cd892ebc --- /dev/null +++ b/.tx/config @@ -0,0 +1,11 @@ +[main] +host = https://www.transifex.com +lang_map = zh-Hans: zh_CN, zh-Hant: zh_TW + +[briar.briar-desktop] +file_filter = src/main/resources/strings/BriarDesktop_<lang>.properties +minimum_perc = 0 +source_file = src/main/resources/strings/BriarDesktop.properties +source_lang = en +type = UNICODEPROPERTIES + diff --git a/build.gradle.kts b/build.gradle.kts index 97a1d8f668b3cf6ca561f4d4020e5e75ab0cbe68..a81a7c85831f46ed8c4c778a0257e0f7a1ed944e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind:2.10.0") implementation("com.github.ajalt:clikt:2.2.0") implementation("org.jetbrains.compose.material:material-icons-extended:0.4.0") + implementation("com.ibm.icu:icu4j:70.1") implementation(project(path = ":briar-core", configuration = "default")) implementation(project(path = ":bramble-java", configuration = "default")) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/Main.kt b/src/main/kotlin/org/briarproject/briar/desktop/Main.kt index d2347fa819a7bf5babfd915804941839b6ee3aec..c922146d59965f19569f421f3847039e27688cc4 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/Main.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/Main.kt @@ -9,6 +9,8 @@ import com.github.ajalt.clikt.parameters.options.option import org.briarproject.bramble.BrambleCoreEagerSingletons import org.briarproject.briar.BriarCoreEagerSingletons import org.briarproject.briar.desktop.utils.FileUtils +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF import java.io.File.separator import java.io.IOException import java.lang.System.getProperty @@ -24,19 +26,19 @@ private val DEFAULT_DATA_DIR = getProperty("user.home") + separator + ".briar" + private class Main : CliktCommand( name = "briar-desktop", - help = "Briar Desktop Client" + help = i18n("main.help.title") ) { - private val debug by option("--debug", "-d", help = "Enable printing of debug messages").flag( + private val debug by option("--debug", "-d", help = i18n("main.help.debug")).flag( default = false ) private val verbosity by option( "--verbose", "-v", - help = "Print verbose log messages" + help = i18n("main.help.verbose") ).counted() private val dataDir by option( "--data-dir", - help = "The directory where Briar will store its files. Default: $DEFAULT_DATA_DIR", + help = i18nF("main.help.data", DEFAULT_DATA_DIR), metavar = "PATH", envvar = "BRIAR_DATA_DIR" ).default(DEFAULT_DATA_DIR) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt index 3a15dc7a06173669c4c87aabbf9a4ef7d6a9a68c..1b01dd7d213e24fe9e55648b6f44e548e6ef6602 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt @@ -24,6 +24,7 @@ import org.briarproject.briar.desktop.theme.selectedCard import org.briarproject.briar.desktop.theme.surfaceVariant import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE import org.briarproject.briar.desktop.ui.HorizontalDivider +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.TimeUtils.getFormattedTimestamp @Composable @@ -68,7 +69,7 @@ fun ContactCard( modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp) ) Text( - if (contactItem.isEmpty) "No messages." else getFormattedTimestamp(contactItem.timestamp), + if (contactItem.isEmpty) i18n("contacts.card.nothing") else getFormattedTimestamp(contactItem.timestamp), fontSize = 10.sp, modifier = Modifier.align(Alignment.Start) ) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt index 70c73a89f615bad46840c163eb99d03bb6b696cf..e14593b5608e282f40495fdda0a23973d47062fe 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.sp +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @Composable fun ContactDropDown( @@ -31,24 +32,24 @@ fun ContactDropDown( onDismissRequest = { isExpanded(false) }, ) { DropdownMenuItem(onClick = { isExpanded(false); onMakeIntroduction() }) { - Text("Make Introduction", fontSize = 14.sp) + Text(i18n("contacts.dropdown.introduction"), fontSize = 14.sp) } DropdownMenuItem(onClick = {}) { - Text("Disappearing Messages", fontSize = 14.sp) + Text(i18n("contacts.dropdown.disappearing"), fontSize = 14.sp) } DropdownMenuItem(onClick = {}) { - Text("Delete all messages", fontSize = 14.sp) + Text(i18n("contacts.dropdown.delete.all"), fontSize = 14.sp) } DropdownMenuItem(onClick = { connectionMode = true; isExpanded(false) }) { Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Text("Connections", fontSize = 14.sp, modifier = Modifier.align(Alignment.CenterVertically)) - Icon(Icons.Filled.ArrowRight, "connections", modifier = Modifier.align(Alignment.CenterVertically)) + Text(i18n("contacts.dropdown.connections"), fontSize = 14.sp, modifier = Modifier.align(Alignment.CenterVertically)) + Icon(Icons.Filled.ArrowRight, i18n("access.contacts.dropdown.connections.expand"), modifier = Modifier.align(Alignment.CenterVertically)) } } DropdownMenuItem(onClick = { contactMode = true; isExpanded(false) }) { Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Text("Contact", fontSize = 14.sp, modifier = Modifier.align(Alignment.CenterVertically)) - Icon(Icons.Filled.ArrowRight, "connections", modifier = Modifier.align(Alignment.CenterVertically)) + Text(i18n("contacts.dropdown.contact"), fontSize = 14.sp, modifier = Modifier.align(Alignment.CenterVertically)) + Icon(Icons.Filled.ArrowRight, i18n("access.contacts.dropdown.contacts.expand"), modifier = Modifier.align(Alignment.CenterVertically)) } } } @@ -58,13 +59,13 @@ fun ContactDropDown( onDismissRequest = { connectionMode = false }, ) { DropdownMenuItem(onClick = { false }) { - Text("Connections", fontSize = 12.sp) + Text(i18n("contacts.dropdown.connections.title"), fontSize = 12.sp) } DropdownMenuItem(onClick = { false }) { - Text("Connect via Bluetooth", fontSize = 14.sp) + Text(i18n("contacts.dropdown.connections.bluetooth"), fontSize = 14.sp) } DropdownMenuItem(onClick = { false }) { - Text("Connect via Removable Device", fontSize = 14.sp) + Text(i18n("contacts.dropdown.connections.removable"), fontSize = 14.sp) } } } @@ -74,13 +75,13 @@ fun ContactDropDown( onDismissRequest = { contactMode = false }, ) { DropdownMenuItem(onClick = { false }) { - Text("Contact", fontSize = 12.sp) + Text(i18n("contacts.dropdown.contact.title"), fontSize = 12.sp) } DropdownMenuItem(onClick = { false }) { - Text("Change contact name", fontSize = 14.sp) + Text(i18n("contacts.dropdown.contact.change"), fontSize = 14.sp) } DropdownMenuItem(onClick = { false }) { - Text("Delete contact", fontSize = 14.sp) + Text(i18n("contacts.dropdown.contact.delete"), fontSize = 14.sp) } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt index 61d13a2f4cd48fa352f4cc2b36cd6909b483624a..735e7f87fb53d822c66b97b0f269d20919890b46 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @Composable fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onContactAdd: () -> Unit) { @@ -28,11 +29,11 @@ fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onCont onValueChange = onValueChange, singleLine = true, textStyle = TextStyle(fontSize = 16.sp, color = MaterialTheme.colors.onSurface), - placeholder = { Text("Contacts") }, + placeholder = { Text(i18n("contacts.search.title")) }, shape = RoundedCornerShape(0.dp), leadingIcon = { val padding = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 12.dp) - Icon(Icons.Filled.Search, "search contacts", padding) + Icon(Icons.Filled.Search, i18n("access.contacts.search"), padding) }, trailingIcon = { IconButton( @@ -40,7 +41,7 @@ fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onCont modifier = Modifier.padding(end = 10.dp).size(32.dp) .background(MaterialTheme.colors.primary, CircleShape) ) { - Icon(Icons.Filled.PersonAdd, "add contact", tint = Color.White, modifier = Modifier.size(20.dp)) + Icon(Icons.Filled.PersonAdd, i18n("access.contacts.add"), tint = Color.White, modifier = Modifier.size(20.dp)) } }, modifier = Modifier.fillMaxSize() diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt index 7a92663a4f2275af8d431a605b3b40cc2d474501..8f28474fe8fe0c501ff327f7348012d715c9da91 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt @@ -28,6 +28,7 @@ import org.briarproject.briar.desktop.theme.outline import org.briarproject.briar.desktop.theme.surfaceVariant import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE import org.briarproject.briar.desktop.ui.HorizontalDivider +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @Composable fun ConversationHeader( @@ -62,7 +63,7 @@ fun ConversationHeader( onClick = { setExpanded(!isExpanded) }, modifier = Modifier.align(Alignment.CenterEnd).padding(end = 16.dp) ) { - Icon(Icons.Filled.MoreVert, "contact info", modifier = Modifier.size(24.dp)) + Icon(Icons.Filled.MoreVert, i18n("access.contact.menu"), modifier = Modifier.size(24.dp)) ContactDropDown(isExpanded, setExpanded, onMakeIntroduction) } HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationInput.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationInput.kt index dcfa6d755ff7af6a179a2a4975f0c68b575e0d30..8818ad76ef78de0a58575aeec1cfe817cc03475e 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationInput.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationInput.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.briarproject.briar.desktop.theme.DarkColors import org.briarproject.briar.desktop.ui.HorizontalDivider +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @Preview @Composable @@ -46,7 +47,7 @@ fun ConversationInput(text: String, updateText: (String) -> Unit, onSend: () -> onValueChange = updateText, maxLines = 10, textStyle = TextStyle(fontSize = 16.sp, lineHeight = 16.sp), - placeholder = { Text("Message") }, + placeholder = { Text(i18n("conversation.message.new")) }, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(0.dp), colors = TextFieldDefaults.textFieldColors( @@ -60,7 +61,7 @@ fun ConversationInput(text: String, updateText: (String) -> Unit, onSend: () -> Modifier.padding(4.dp).size(32.dp) .background(MaterialTheme.colors.primary, CircleShape), ) { - Icon(Icons.Filled.Add, "add attachment", Modifier.size(24.dp), Color.White) + Icon(Icons.Filled.Add, i18n("access.attachment"), Modifier.size(24.dp), Color.White) } }, trailingIcon = { @@ -69,7 +70,7 @@ fun ConversationInput(text: String, updateText: (String) -> Unit, onSend: () -> ) { Icon( Icons.Filled.Send, - "send message", + i18n("access.message.send"), tint = MaterialTheme.colors.secondary, modifier = Modifier.size(24.dp) ) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/TextBubble.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/TextBubble.kt index 63fc4c7f177b26799dd00baf65322f7e41be3938..47f6711eb5ce775da9a575d60b64f0b315c0b551 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/TextBubble.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/TextBubble.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.briarproject.briar.desktop.theme.awayMsgBubble import org.briarproject.briar.desktop.theme.localMsgBubble +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.TimeUtils @Composable @@ -47,7 +48,7 @@ fun TextBubble(m: ConversationMessageItem) { if (m.isSeen) Icons.Filled.DoneAll // acknowledged else if (m.isSent) Icons.Filled.Done // sent else Icons.Filled.Schedule // waiting - Icon(icon, "sent", modifier) + Icon(icon, i18n("access.message.sent"), modifier) } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt b/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt index 68b78c670fc047af6545ea71ae84335b2ca1c28c..8716c44ef85401d10401dcdc55c33f028588690e 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt @@ -29,6 +29,9 @@ import org.briarproject.briar.desktop.contact.ContactCard import org.briarproject.briar.desktop.contact.ProfileCircle import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE import org.briarproject.briar.desktop.ui.HorizontalDivider +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF +import java.util.Locale @Composable fun ContactDrawerMakeIntro( @@ -43,10 +46,10 @@ fun ContactDrawerMakeIntro( onClick = { setInfoDrawer(false) }, Modifier.padding(horizontal = 11.dp).size(32.dp).align(Alignment.CenterVertically) ) { - Icon(Icons.Filled.Close, "close make intro screen") + Icon(Icons.Filled.Close, i18n("access.introduction.close")) } Text( - text = "Introduce ${viewModel.firstContact.value!!.author.name} to", + text = i18nF("introduction.title_first", viewModel.firstContact.value!!.author.name), fontSize = 16.sp, modifier = Modifier.align(Alignment.CenterVertically) ) @@ -70,10 +73,10 @@ fun ContactDrawerMakeIntro( onClick = viewModel::backToFirstScreen, Modifier.padding(horizontal = 11.dp).size(32.dp).align(Alignment.CenterVertically) ) { - Icon(Icons.Filled.ArrowBack, "go back to make intro contact screen", tint = Color.White) + Icon(Icons.Filled.ArrowBack, i18n("access.introduction.back.contact"), tint = Color.White) } Text( - text = "Introduce Contacts", + text = i18n("introduction.title_second"), fontSize = 16.sp, modifier = Modifier.align(Alignment.CenterVertically) ) @@ -83,7 +86,7 @@ fun ContactDrawerMakeIntro( ProfileCircle(36.dp, viewModel.firstContact.value!!.author.id.bytes) Text(viewModel.firstContact.value!!.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp) } - Icon(Icons.Filled.SwapHoriz, "swap", modifier = Modifier.size(48.dp)) + Icon(Icons.Filled.SwapHoriz, i18n("access.swap"), modifier = Modifier.size(48.dp)) Column(Modifier.align(Alignment.CenterVertically)) { ProfileCircle(36.dp, viewModel.secondContact.value!!.author.id.bytes) Text(viewModel.secondContact.value!!.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp) @@ -93,7 +96,7 @@ fun ContactDrawerMakeIntro( TextField( viewModel.introductionMessage.value, viewModel::setIntroductionMessage, - placeholder = { Text(text = "Add a message (optional)") }, + placeholder = { Text(text = i18n("introduction.message")) }, ) } Row(Modifier.padding(8.dp)) { @@ -101,7 +104,8 @@ fun ContactDrawerMakeIntro( onClick = { setInfoDrawer(false) }, Modifier.fillMaxWidth() ) { - Text("MAKE INTRODUCTION") + val text = i18n("introduction.introduce") + Text(text.uppercase(Locale.getDefault())) } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/Login.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/Login.kt index 488a225e8a53532798573f64f6b77d06ff4731f6..bfc6ff0d8360769bfcc78f6b71961fc0b5758333 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/login/Login.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/login/Login.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n // TODO: Error handling @OptIn(ExperimentalComposeUiApi::class) @@ -61,7 +62,7 @@ fun Login( OutlinedTextField( value = viewModel.password.value, onValueChange = viewModel::setPassword, - label = { Text("Password") }, + label = { Text(i18n("password")) }, singleLine = true, visualTransformation = PasswordVisualTransformation(), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done), @@ -77,7 +78,7 @@ fun Login( ) Spacer(Modifier.height(16.dp)) Button(onClick = { signIn() }) { - Text("Login") + Text(i18n("login.login")) } DisposableEffect(Unit) { @@ -90,4 +91,4 @@ fun Login( @Composable fun BriarLogo(modifier: Modifier = Modifier.fillMaxWidth().clip(shape = RoundedCornerShape(400.dp))) = - Image(painterResource("images/logo_circle.svg"), "Briar logo", modifier) + Image(painterResource("images/logo_circle.svg"), i18n("access.logo"), modifier) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/Registration.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/Registration.kt index 78758000be6c01d520982cb4c7569d856ecb5f03..e90a0d6c1f378e23c39fa21aeb9db3b165c96c1d 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/login/Registration.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/login/Registration.kt @@ -33,6 +33,8 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import java.util.Locale // TODO: Error handling and password strength @OptIn(ExperimentalComposeUiApi::class) @@ -61,7 +63,7 @@ fun Registration( OutlinedTextField( value = viewModel.username.value, onValueChange = { viewModel.setUsername(it) }, - label = { Text("Username") }, + label = { Text(i18n("registration.username")) }, singleLine = true, textStyle = TextStyle(color = Color.White), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), @@ -78,7 +80,7 @@ fun Registration( OutlinedTextField( value = viewModel.password.value, onValueChange = { viewModel.setPassword(it) }, - label = { Text("Password") }, + label = { Text(i18n("password")) }, singleLine = true, textStyle = TextStyle(color = Color.White), visualTransformation = PasswordVisualTransformation(), @@ -93,7 +95,8 @@ fun Registration( ) Spacer(Modifier.height(16.dp)) Button(onClick = { signUp() }) { - Text("Register", color = Color.Black) + val text = i18n("registration.register") + Text(text.uppercase(Locale.getDefault()), color = Color.Black) } DisposableEffect(Unit) { diff --git a/src/main/kotlin/org/briarproject/briar/desktop/settings/PlaceHolderSettingsView.kt b/src/main/kotlin/org/briarproject/briar/desktop/settings/PlaceHolderSettingsView.kt index 7ad692bb97924fc74714c15ccefda2344db6836d..0c94d09f3d02338c81f70f59dc0d06e4a5d982a3 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/settings/PlaceHolderSettingsView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/settings/PlaceHolderSettingsView.kt @@ -7,13 +7,14 @@ import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @Composable fun PlaceHolderSettingsView(isDark: Boolean, setDark: (Boolean) -> Unit) { Surface(Modifier.fillMaxSize()) { Box { Button(onClick = { setDark(!isDark) }) { - Text("Change Theme") + Text(i18n("settings.theme")) } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt index 76c607b4402f510e41e39042c662147bc862f4f8..3d46cd5d7ce2685ec0e1dd18e9f64131bcedff7d 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt @@ -19,6 +19,7 @@ import org.briarproject.briar.desktop.login.Registration import org.briarproject.briar.desktop.login.RegistrationViewModel import org.briarproject.briar.desktop.navigation.SidebarViewModel import org.briarproject.briar.desktop.theme.BriarTheme +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import java.awt.Dimension import java.util.logging.Logger import javax.annotation.concurrent.Immutable @@ -69,7 +70,7 @@ constructor( override fun start() { application { val (isDark, setDark) = remember { mutableStateOf(true) } - val title = "Briar Desktop" + val title = i18n("main.title") var screenState by remember { mutableStateOf( if (accountManager.hasDatabaseKey()) { diff --git a/src/main/kotlin/org/briarproject/briar/desktop/utils/InternationalizationUtils.kt b/src/main/kotlin/org/briarproject/briar/desktop/utils/InternationalizationUtils.kt new file mode 100644 index 0000000000000000000000000000000000000000..f2928561d8ed2f056c9e6a90e2f55638f673475f --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/utils/InternationalizationUtils.kt @@ -0,0 +1,47 @@ +package org.briarproject.briar.desktop.utils + +import com.ibm.icu.text.MessageFormat +import java.util.Locale +import java.util.ResourceBundle + +/** + * Helper functions around internationalization. + * + * Instead of using the default Locale, one can also use, e.g., `Locale("ar")` instead + * or call `Locale.setDefault()`. + */ +object InternationalizationUtils { + + /** + * Returns the translated text of the string identified with `key` + */ + fun i18n(key: String): String { + val resourceBundle = createResourceBundle() + return resourceBundle.getString(key) + } + + /** + * Returns the translated text of a string with plurals identified with `key` + */ + fun i18nP(key: String, amount: Int): String { + val pattern: String = i18n(key) + val messageFormat = MessageFormat(pattern, Locale.getDefault()) + return messageFormat.format(arrayOf(amount)) + } + + /** + * Returns the translated text of a formatted string with + */ + fun i18nF(key: String, vararg params: Any): String { + val pattern: String = i18n(key) + return java.text.MessageFormat.format(pattern, *params) + } + + /** + * Returns the resource bundle used for i18n at Briar Desktop + */ + private fun createResourceBundle(): ResourceBundle { + val locale = Locale.getDefault() + return ResourceBundle.getBundle("strings.BriarDesktop", locale) + } +} diff --git a/src/main/resources/strings/BriarDesktop.properties b/src/main/resources/strings/BriarDesktop.properties new file mode 100644 index 0000000000000000000000000000000000000000..535f818d015728f22b0b1e0bbfd894f1a7bd6b9c --- /dev/null +++ b/src/main/resources/strings/BriarDesktop.properties @@ -0,0 +1,57 @@ +# Accessibility +access.attachment=Add attachment +access.contacts.add=Add contact +access.contact.menu=Show contact menu +access.contacts.dropdown.connections.expand=Expand connections menu +access.contacts.dropdown.contacts.expand=Expand connections menu +access.contacts.search=Icon search contacts +access.introduction.back.contact=Go back to contact screen of introduction process +access.introduction.close=Close introduction screen +access.message.send=Send message +access.message.sent=Message sent +access.logo=Briar logo +access.swap=Icon showing errors between two contacts + +# Contacts +contacts.card.nothing=No messages. +contacts.dropdown.connections=Connections +contacts.dropdown.connections.title=Connections +contacts.dropdown.connections.bluetooth=Connect via Bluetooth +contacts.dropdown.connections.removable=Connect via Removable Drive +contacts.dropdown.contact=Contact +contacts.dropdown.contact.title=Contact +contacts.dropdown.contact.change=Change contact name +contacts.dropdown.contact.delete=Delete contact +contacts.dropdown.delete.all=Delete all messages +contacts.dropdown.disappearing=Disappearing messages +contacts.dropdown.introduction=Make Introduction +contacts.search.title=Contacts + +# Conversation +conversation.message.new=New Message + +# Introduction +introduction.introduce=Make Introduction +introduction.message=Add a message (optional) +introduction.title_first=Introduce {0} to +introduction.title_second=Introduce Contacts + +# Login screen +login.login=Login + +# Main +main.title=Briar Desktop +main.help.title=Briar Desktop Client +main.help.debug=Enable printing of debug messages +main.help.verbose=Print verbose log messages +main.help.data=The directory where Briar will store its files. Default: {0} + +# Miscellaneous +password=Password + +# Registration screen +registration.username=Username +registration.register=Create Account + +# Settings +settings.theme=Change Theme \ No newline at end of file diff --git a/src/main/resources/strings/BriarDesktop_ar.properties b/src/main/resources/strings/BriarDesktop_ar.properties new file mode 100644 index 0000000000000000000000000000000000000000..11b29ba8d5b4d80b3558e7b97a7463bde7cae0cb --- /dev/null +++ b/src/main/resources/strings/BriarDesktop_ar.properties @@ -0,0 +1,57 @@ +# Accessibility +access.attachment=Ø¥Ø¶Ø§ÙØ© مرÙÙ‚ +access.contacts.add=Ø¥Ø¶Ø§ÙØ© جهة إتصال +access.contact.menu=Show contact menu +access.contacts.dropdown.connections.expand=Expand connections menu +access.contacts.dropdown.contacts.expand=Expand connections menu +access.contacts.search=Icon search contacts +access.introduction.back.contact=Go back to contact screen of introduction process +access.introduction.close=Close introduction screen +access.message.send=Send message +access.message.sent=تم إرسال الرسالة +access.logo=Briar logo +access.swap=Icon showing errors between two contacts + +# Contacts +contacts.card.nothing=لا رسائل. +contacts.dropdown.connections=شبكات الاتصال +contacts.dropdown.connections.title=شبكات الاتصال +contacts.dropdown.connections.bluetooth=الإتصال عبر بلوتوث +contacts.dropdown.connections.removable=Connect via Removable Drive +contacts.dropdown.contact=للتواصل +contacts.dropdown.contact.title=للتواصل +contacts.dropdown.contact.change=تعديل إسم جهة الاتصال +contacts.dropdown.contact.delete=ØØ°Ù جهة الإتصال +contacts.dropdown.delete.all=ØØ°Ù جميع الرّسائل +contacts.dropdown.disappearing=Disappearing messages +contacts.dropdown.introduction=عمل تقديم +contacts.search.title=Ø¯ÙØªØ± الاتصالات + +# Conversation +conversation.message.new=رسالة جديدة + +# Introduction +introduction.introduce=عمل تقديم +introduction.message=Ø§Ø¶Ø§ÙØ© رسالة (إختياري) +introduction.title_first=Introduce {0} to +introduction.title_second=قدّم جهات اتصال + +# Login screen +login.login=الدخول + +# Main +main.title=Briar Desktop +main.help.title=Briar Desktop Client +main.help.debug=Enable printing of debug messages +main.help.verbose=Print verbose log messages +main.help.data=The directory where Briar will store its files. Default: {0} + +# Miscellaneous +password=كلمة السّر + +# Registration screen +registration.username=اسم المستخدم +registration.register=إنشاء Ø§Ù„ØØ³Ø§Ø¨ + +# Settings +settings.theme=Change Theme \ No newline at end of file diff --git a/src/main/resources/strings/BriarDesktop_de.properties b/src/main/resources/strings/BriarDesktop_de.properties new file mode 100644 index 0000000000000000000000000000000000000000..837aea2ab8efee5ee2acfe14cabdd74c50e8899a --- /dev/null +++ b/src/main/resources/strings/BriarDesktop_de.properties @@ -0,0 +1,57 @@ +# Accessibility +access.attachment=Anhang hinzufügen +access.contacts.add=Kontakt hinzufügen +access.contact.menu=Show contact menu +access.contacts.dropdown.connections.expand=Expand connections menu +access.contacts.dropdown.contacts.expand=Expand connections menu +access.contacts.search=Icon search contacts +access.introduction.back.contact=Go back to contact screen of introduction process +access.introduction.close=Close introduction screen +access.message.send=Nachricht senden +access.message.sent=Nachricht gesendet +access.logo=Briar logo +access.swap=Icon showing errors between two contacts + +# Contacts +contacts.card.nothing=Keine Nachrichten. +contacts.dropdown.connections=Verbindungen +contacts.dropdown.connections.title=Verbindungen +contacts.dropdown.connections.bluetooth=Über Bluetooth verbinden +contacts.dropdown.connections.removable=Über Wechseldatenträger verbinden +contacts.dropdown.contact=Kontakt +contacts.dropdown.contact.title=Kontakt +contacts.dropdown.contact.change=Kontaktnamen ändern +contacts.dropdown.contact.delete=Kontakt löschen +contacts.dropdown.delete.all=Alle Nachrichten löschen +contacts.dropdown.disappearing=Selbstlöschende Nachrichten +contacts.dropdown.introduction=Kontaktempfehlung abgeben +contacts.search.title=Kontakte + +# Conversation +conversation.message.new=Neue Nachricht + +# Introduction +introduction.introduce=Kontaktempfehlung abgeben +introduction.message=Nachricht hinzufügen (optional) +introduction.title_first=Introduce {0} to +introduction.title_second=Kontakte untereinander bekannt machen + +# Login screen +login.login=Anmelden + +# Main +main.title=Briar Desktop +main.help.title=Briar Desktop Client +main.help.debug=Enable printing of debug messages +main.help.verbose=Print verbose log messages +main.help.data=The directory where Briar will store its files. Default: {0} + +# Miscellaneous +password=Passwort + +# Registration screen +registration.username=Benutzername +registration.register=Konto erstellen + +# Settings +settings.theme=Change Theme \ No newline at end of file diff --git a/src/main/resources/strings/BriarDesktop_es.properties b/src/main/resources/strings/BriarDesktop_es.properties new file mode 100644 index 0000000000000000000000000000000000000000..38f2660951f514d5f7ca71dbd95b3f0e4353aa2a --- /dev/null +++ b/src/main/resources/strings/BriarDesktop_es.properties @@ -0,0 +1,57 @@ +# Accessibility +access.attachment=Añadir adjunto +access.contacts.add=Añadir el contacto +access.contact.menu=Show contact menu +access.contacts.dropdown.connections.expand=Expand connections menu +access.contacts.dropdown.contacts.expand=Expand connections menu +access.contacts.search=Icon search contacts +access.introduction.back.contact=Go back to contact screen of introduction process +access.introduction.close=Close introduction screen +access.message.send=Enviar mensaje +access.message.sent=Mensaje enviado +access.logo=Briar logo +access.swap=Icon showing errors between two contacts + +# Contacts +contacts.card.nothing=Sin mensajes. +contacts.dropdown.connections=Conexiones +contacts.dropdown.connections.title=Conexiones +contacts.dropdown.connections.bluetooth=Conectar mediante Bluetooth +contacts.dropdown.connections.removable=Conectar a través de Discos Removibles +contacts.dropdown.contact=Contacto +contacts.dropdown.contact.title=Contacto +contacts.dropdown.contact.change=Cambiar nombre del contacto +contacts.dropdown.contact.delete=Eliminar contacto +contacts.dropdown.delete.all=Eliminar todos los mensajes +contacts.dropdown.disappearing=Mensajes con caducidad +contacts.dropdown.introduction=Hacer presentación +contacts.search.title=Contactos + +# Conversation +conversation.message.new=Nuevo mensaje + +# Introduction +introduction.introduce=Hacer presentación +introduction.message=Añade un mensaje (opcional) +introduction.title_first=Introduce {0} to +introduction.title_second=Presentar contactos + +# Login screen +login.login=Entrar + +# Main +main.title=Briar Desktop +main.help.title=Briar Desktop Client +main.help.debug=Enable printing of debug messages +main.help.verbose=Print verbose log messages +main.help.data=The directory where Briar will store its files. Default: {0} + +# Miscellaneous +password=Contraseña + +# Registration screen +registration.username=Nombre de usuario +registration.register=Crear cuenta + +# Settings +settings.theme=Change Theme \ No newline at end of file