📜 ⬆️ ⬇️

LocoLaser: translate apps in Google Sheets



In practice, it is often necessary to release applications immediately on Android and iOS, and sometimes on Windows Phone. In this case, some developers solve the problem of localization through, translating each platform separately. Agree, not the best trip. At the time when I asked for a solution to this problem, utilities for downloading string resources from Google Sheets could already be found on the network, but most of them looked at least clumsy. I was not satisfied with such a disposition. As a result, LocoLaser was born - an easy-to-use, but very smart Java utility. For a couple of years, I used it exclusively in my work projects. During this time, she managed to acquire a fairly rich functionality, and is now ready to be presented to the public.

In this article I will talk about what LocoLaser is and how to integrate it into your project. First, let's take a look at the utility's capabilities, and then move on to more specific things, such as the Gradle plugin for Android and Bash scripts for iOS. I'm a lazy programmer enough to do the same actions from time to time, so everything is done in such a way that after the initial setup, your further work will be limited to starting the necessary task when it is needed. For translators, the translation looks quite simple, we share Google table with lines for them and they enter the translation in the corresponding columns. If you need to add a language, simply add another column with the new language. Google Sheets allows you to individually customize access to table editing, so each translator has access only to columns with their own languages.

')
WARNING
Since the publication of the article, the library has undergone some significant changes and some of the points described in this article are no longer relevant. You can find the latest instructions and examples in the repositories listed at the end of this article.


configuration file


But back to the utility. If too simplified, she herself is a regular jar file. You can download it from the Bintray repository . The easiest way to start the utility is to start from the console. The console command might look like this:

java -jar loco-laser-google.jar localization_config.json 
Remark number 1
To execute the command, Java must be installed on the machine.
Remark number 2
Once again, I don’t need to download jar directly to use the utility in my projects. For these purposes, there are ready-made scripts and plugins.
After executing the command, the corresponding resource files will be created and placed in the necessary folders. The first parameter must be the path to the configuration file. In the example above, this is the file "localization_config.json" . It is he who is responsible for setting all the intricacies of import and export. The file is a textual JSON which may look, for example, like this:

 { "platform": "android", "source": { "id":"1JZxUcu30BjxLwHg12bdHTxjDgsGFX9HA9zC4Jd8cuUM", "column_key":"key", "column_locales":["base", "ru"] } } 

This example contains a minimum of parameters to run. Let's see what they mean.

The "platform" parameter specifies the platform for which the import will be made. One of 2 options is possible: "android" and "ios" (Windows Phone is not supported yet, but it is only a matter of time). In addition to the format, the platform is responsible for the location of files with resources, their names, as well as a place for storing temporary files.

The "source" parameter is a JSON object. Its contents are responsible for where and how the lines will be loaded. The minimum set of parameters for it will be: "id" , "column_key" and "column_locales" .

As the "source.id" , a Google spreadsheet identifier must be specified. In order to find this identifier you need to look at the URL to the table. It should look like this docs.google.com/spreadsheets/d/1JZxUcu30BjxLwHg12bdHTxjDgsGFX9HA9zC4Jd8cuUM . In this example, the identifier will be "1JZxUcu30BjxLwHg12bdHTxjDgsGFX9HA9zC4Jd8cuUM" . By the way, the link is clickable and by it you can see a real example of a table.

With the remaining parameters, everything is quite simple, "source.column_key" contains the name of the column with keys, and "source.column_locales" array of column names with values, where the column name is also a locale. For the base locale, the word "base" reserved.

Here it is necessary to make a small digression and decide on the structure of the table and at the same time deal with the fact that such column names. If it is simple, then LocoLaser perceives the first “not crossed out” line as a line with the names of columns. You ask "What else for the deleted line?". The fact is that the very first column in the table is used for official purposes. At the moment, you can "cross out" the line by adding the "-" symbol in the first column. The “crossed out” line is completely ignored and in no way participates in localization. If you look at the table at the link above, you will see that the first row with “human” headings is “crossed out” and is only a decorative element of the table. You can also “strike out” lines with resources, for example, when a line is not used anywhere, but it’s a pity to delete.



Full list of parameters


The above parameters are enough for the utility to work. However, there are others. I will not describe each in detail, but only provide their full list in the form of tables.

Source
ParameterType ofDescription
idStringID Sheet Google Sheet. The URL to the table contains this identifier (https://docs.google.com/spreadsheets/d/*sheet_id*). Required.
column_keyStringThe name of the column with the keys. Required.
column_localesStrings arrayAn array of column names with values ​​where the column name is also a locale. For the base locale, use the column named "base" . Required.
column_quantityStringThe name of the column with the quantitative values. Cells in the table must contain one of the following values: zero , one , two , few , many , other . An empty string is perceived as other. Optional parameter. By default, quantitative values ​​are not used.
column_commentStringThe name of the column with comments. Optional parameter. By default, comments in the resource file are not recorded.
worksheet_titleStringThe name of the sheet in the Google Sheet table. Optional parameter. The default is the first sheet.
credential_fileStringThe path to the file containing the credentials for OAuth authorization. When using a relative path, the file path is specified relative to the working directory. Optional parameter.
typeStringType of data source. Must be "googlesheet" . Optional parameter. However, if you want to be sure that the configuration file and the utility work with the same type of source, you should specify its type.
Platform
As I said earlier, the platform sets a number of parameters specific to it. These parameters can be changed if the platform is defined not in the form of a string, as was done above, but in the form of a JSON object. Here is a list of the possible properties of this object:
ParameterType ofDescription
typeStringType of platform. The possible values ​​are: "android" or "ios" . Required.
res_nameStringThe name of the resource file without the extension. The extension is selected depending on the type of file. Optional parameter.
Default values:
Android - "strings"
iOS - "Localizable"
res_dirStringPath to the resource directory. Optional parameter.
Default values:
Android - "./src/main/res/"
iOS - "./"
temp_dirStringPath to the directory for storing temporary files. Optional parameter.
Default values:
Android - "./build/tmp/"
iOS - "../DerivedData/LocoLaserTemp/"
Important!
All relative paths are relative to the working directory. By default, the configuration file directory is used as the working directory.
Other parameters
The configuration may contain several parameters besides the platform and the source. All of them can significantly affect the result:
ParameterType ofDescription
work_dirStringThe path to the working folder. All relative paths are relative to this folder. The default is the configuration file directory.
force_importBooleanThe utility remembers the state of resources at the last execution and tries not to start the import unnecessarily. To ignore this and perform the import always in full, set force_import to true . The default is false.
conflict_strategyStringDetermine how to resolve conflicts when combining platform resources and resources from Google Sheets. There are 3 options:
  • remove_platform - Removes local platform resources by completely replacing them with resources from Google Sheets.
  • keep_new_platform - Saves platform strings if they are missing from Google Sheet.
  • export_new_platform - Saves new platform lines and uploads them to Google Sheet.
The default keep_new_platform .
duplicate_commentsBooleanIf duplicate_comments = false comment will not be added to the resource file if this comment is equal to the localized string. The default is false .
delayLongThe time in minutes determines the minimum time to the next localization. Localization will not be performed more often than specified in the delay parameter. If force_import used force_import delay is ignored.

The duplicate_comments parameter is especially useful if you use the base locale as a comment. In this case, there will always be a comment in the localized resources with text in the base language (usually English), and in the base resource there will be no comments, since in this case they repeat the string values ​​and are redundant.

Sample Android resources with duplicate_comments = false
/values/strings.xml
 <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">LocoLaser example</string> <string name="screen_main_app_description">This is example application of how to use the LocoLaser.</string> <string name="screen_main_plural_example_title">Plural string examples</string> </resources> 
/values-ru/strings.xml
 <?xml version="1.0" encoding="utf-8"?> <resources> /* LocoLaser example */ <string name="app_name">LocoLaser </string> /* This is example application of how to use the LocoLaser. */ <string name="screen_main_app_description">     LocoLaser.</string> /* Plural string examples */ <string name="screen_main_plural_example_title"> Plural </string> </resources> 
Customized configuration example localization_config.json
 { "platform": { "type":"android", "res_name":"strings_intro" }, "source": { "type":"googlesheet", "id":"1JZxUcu30BjxLwHg12bdHTxjDgsGFX9HA9zC4Jd8cuUM", "column_key":"key", "column_locales":["base", "ru"], "column_comment":"base", “worksheet_title”:”Strings intro” }, "force_import":true, "conflict_strategy":”keep_new_platform”, "delay":60, } 

Console arguments


When launching the localizer from the console, you can change some configuration parameters. To do this, add the appropriate arguments. Important: the path to the configuration file is always listed first. The order of the remaining arguments is irrelevant.
ArgumentDescription
--force or --fFlag. Sets "force_import = true"
-cs *string conflict strategy*Overrides property "conflict_strategy"
-delay *long delay*Overrides property "delay"

Command example

 java -jar loco-laser-google.jar localization_config.json --f -cs keep_new_platform 

Android and Gradle Plugin


When working with LocoLaser in Android, it is worth using a special Gradle plugin. He adds to the project several tasks united in the “localization” group . There are 2 options for connecting the plugin, "classic" and "alternative".

The classic way to connect the plugin
Open the root project's "build.gradle" file and add the following lines:
 buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath "gradle.plugin.ru.pocketbyte.locolaser:plugin:1.0.1" } } 

After that, open the application module's "build.gradle" file and add one line to the beginning of the file:

 apply plugin: "ru.pocketbyte.locolaser" 

Alternative way to connect the plugin
This method is suitable for projects with the Gradle version above 2.1 and uses the so-called “incubating” functionality (use it at your own risk, the Gradle team warns us). For it, you do not need to register anything in the root "build.gradle" . Open the "build.gradle" module you need and at the very top of the file add the following line

 plugins { id "ru.pocketbyte.locolaser" version "1.0.1" } 

Once again, the line should be at the very top. There should be nothing else in front of her. Otherwise, the project is not even synchronized.

After connecting the plugin, one of the above methods, in the same file, add the following dependency:

 dependencies { ... localize 'ru.pocketbyte.locolaser:locolaser-mobile-googlesheet:1.1.+' } 

Now it only remains to put the configuration file in the application module folder and you can start working. By default, the plugin uses a file named "localize_config.json" as the configuration file. After the project is synchronized, the “localization” group will appear in the Gradle list of tasks, with 3 tasks in it.



localize - Runs LocoLaser with standard parameters;
localizeForce - Launches LocoLaser with "--force" flag;
localizeExportNew - Runs the LocoLaser with the flag "--force" and "conflict_strategy" = "export_new_platform" .

If you want localization to be run on each build, add a dependency to the preBuild task:

 afterEvaluate { preBuild.dependsOn project.tasks.localize } 

Sample Android project on GitHub: github.com/PocketByte/locolaser-android-example
Used Gradle plugin. Localization is built into the build process.

Localization of iOS applications


Unlike Android, iOS doesn’t have such a powerful build system as Gradle, so you have to use workaround methods, since there’s plenty of them and they fit perfectly into the iOS ideology. I suggest storing the configuration file in the source folder, usually the name of this folder coincides with the name of the project (this is the only location where it is possible to use default values).

So, you created the configuration file and put it in the source folder. How to use it? Since the utility artifacts are located on the remote maven repository, I went along the path of writing a bash script that mimics the dependency management system. Here he is:

localize.command
 GROUP="ru/pocketbyte/locolaser" ARTIFACT="locolaser-mobile-googlesheet" VERSION="1.1.1" CONFIG_FILE="localization_config.json" cd "`dirname \"$0\"`" ARTIFACTS_DIR="../DerivedData/LocoLaserTemp/artifacts/$GROUP/" mkdir -p $ARTIFACTS_DIR ARTIFACT_FILE="$ARTIFACTS_DIR$ARTIFACT-$VERSION.jar" if [ -f $ARTIFACT_FILE ] then echo "Artifact already downloaded" else ARTIFACT_URL="https://bintray.com/pocketbyte/maven/download_file?file_path=$GROUP/$ARTIFACT/$VERSION/$ARTIFACT-$VERSION.jar" echo "Loading: $ARTIFACT_URL" curl -L -o $ARTIFACT_FILE $ARTIFACT_URL if [ $? -eq 0 ] then echo "Artifact downloaded" else exit $? fi fi java -jar $ARTIFACT_FILE $CONFIG_FILE 

This script downloads the jar file from the remote maven repository and places it in the folder "../DerivedData/LocoLaserTemp/artifacts/" , and then launches it for execution. The configuration file is a file named "localize_config.json" , as in Android. The solution is quite rough, you will have to specify the version of the artifact used. However, it copes well with its task and does not need to store the utility file in the jar project. As you can guess from relative paths, the script should be saved in the same folder as the configuration file. I have several such scripts in my project: localize.command , localizeForce.command and localizeExportNew.command . All these commands repeat the tasks from the Gradle plugin above.

If you want localization to be performed on each build, add the appropriate Run scripts :



The run script should follow the Target Dependencies phase immediately . This is important because if you import new lines, for example, after the Copy Bundle Resources phase, you will get the desired result with a delay, only in the next build.

Sample iOS project on GitHub: github.com/PocketByte/locolaser-ios-example
Used bash script. Localization is built into the build process.

Windows Phone and other platforms


Unfortunately, I have absolutely no development experience for this platform, and in the organization in which I currently work, the development of Windows Phone applications has not been practiced for a long time. I sincerely apologize for posting an article in the Windows Phone hub. However, I have not found a better way to attract the attention of Windows Phone developers. So if you are an experienced Windows Phone developer, and you have something to tell me about this topic, please contact me. Also, if any of the readers have an idea about using LocoLaser in other platforms or services, feel free to write, I am open to suggestions.

Credential file


To access Google tables, you need to pass OAuth authorization. If the "source.credential_file" parameter is not specified in the configuration file, the browser with the Google authentication page will automatically open when the localizer starts. After successful authorization, the utility will continue its work. Next time you will not be required to log in to this table.

Since the above approach requires user intervention, it is not suitable for situations where the localization process should take place in automatic mode. In order for the process to always run automatically, you should specify credential_file . As a credential file, the service Google Account file will do. To create it in the Google Developers console, you need to do the following:

  1. Create a new project if it has not yet been created for your application.
  2. View the list of service accounts . If you are prompted to select a project, select the project for your application.
  3. Click Create Service Account .
  4. In the "Create a service account" window , enter the name of the new service account, enable the "Create a new private key" flag, select the JSON key type. The role of the account is not necessary to choose. Click Create .

The service account will be created, after which the private key file will be automatically downloaded. The path to this file should be specified as the parameter "source.credential_file" . In order for a service account to have access to a Google spreadsheet, it is necessary to share it for a previously created service account. When sharing, specify the Service Account ID as Email. This identifier can be seen in the list of service accounts, or peeped in the downloaded key file.

Sample service key file
 { "type": "service_account", "project_id": "myapp-1086", "private_key_id": "b67c2edcc47c7053c035d8681c8eb7e9f4d90c09", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJUV49mSQB2NSO\nw+tfQWq4pP63U4t7W8V6O6E7FABbYS5N4g35nRzVEj5NciqI27shHSKVrsl7U5ji\nM0IIA+vi+dgHXwHfCPhS9d85xZ73fuqFaj29iru+pTq9tuNieLDl60L04oCc1qQKBgQDLsMMnX2r9qkQw01H5L2WRB9R6er+bO4DE\n8Ecpripfd/e7qq89WcGu1H8S+3Jy49dBmN709vPvVcsGQx8mDdYdm4P0WPkKbgTo\nt12OA07uiY6Zn54rW5CXrjdoscPXB94AS2ps4M3/xY5hHTxwtS8yJxoUgTVEfgNB\nDwzNrZSCVEMCoBAIYl6rWGITgNaR0+FLuP+kjZw\nHsLdkU8173J3nhuYhxo7N90BhO08lquIQ7bJAoGBAKLz\nVRxRdFlcdlMNK34K0dkVh4E4Y8K+9oQWqQeKIrHfWpuSr8CH5q+Dpek8qVGKPnFm\n567XRUzJuLLYzbl2xj1HZWf8KbeTTnALKYg8Jz\nxXXvLlZl2OJ8Frr9ry1DEszPkwWwTQJg5bRG7Z//QfpyEZ2PUvpCNVVpeRuMmUhv\nu5rSLa0G+C97/XIGz/O/1ME9WXU6ZNRwwDkSDw6L7AIrXY8V+8pIRL9e0ks4Uw/A\n6ACYrlYMYYAIl79MNrUrizvF5KwxLiohHJ5KVpThGuRZDaidCPp9BL/h8tfhXPel\nwQot9dM8P4CmQNR/fMpytQSVk7vv95B2JHrt6QmIsQKBgQD9BJ8gfZUVhlxtuaWO\nMKl1PjiD+YKhz4rmIZUKM84xphsGYUBhH29s1zb98u9vlEnlx3bGUtDakNnjTDQp\nagQv22+6STL+0s1haOxyfbi1jIzXvzh47yij6+v7WEIdNj45WV9kpcFTi2oUXURt\nEi3WskYTijYGbDNQmpG2kmY\nDz0KdeTFJxsnFstTT/VozEGvNIHf+8PhKv0123dBFuqSgBD5SFHDp3tQ2IzC81Jm\n5FJLldk3Hw1QRh6+WiEJBTX6nFU4DB5tVXKhbPOvhqYwI/CUYWWbVBQCQgqURcyr\nUdRxAHyDrxKhNrmXXKAEwR0rDz2uGTQCfJ0Zyk/Z1E7iDl/SDfYSSD70wAgGblH2\nAAIQzeoPAgMBAAECggEAb7Trswhft3qmb1V9LEzzN+OtxvHfqqKAkFO4Ijz1+b6R\n3/t4P7KTRhOqaHTZ7zjlu/kbsKzc9casRY+lqybp4/c4jNaGBklG6Vmu96E9wKBgEFPsRe88v/UaAV213Jlw0hdYE9I19yW6z7OSl+Q0dflDbLO5cRs\nTeHlh+9zhzThLVYf79vvwrO4klXm9Mv/7sa/uQ54GK7IkXVklSxUoZpThoOme5hT\n+8ScgJSnyqEpwFQjaslbNBUxtpc9IA2bseP1S7aCVDfZtEp7rHqOFZTvSgol5YD/AoGAdIMNKvg0IiS2xcIEbyHa\nPTNrUfpHeJk/P+Frr1cmDHhGe1l0FWNg9EDlhItAW5EP15ubZPdWQRrV7RuydJlc\ngWesc2RLPIG8+so1TpG1F62+tsQ9lGSF5imiew1x7sQ3H0VIpGtSfTvSMep6fuE7\niuSVbY0UnpxGnqzo9TBAYS4=\n-----END PRIVATE KEY-----\n", "client_email": "localizator@myapp-1086.iam.gserviceaccount.com", "client_id": "704739729071909788554", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/localizator%40myapp-1086.iam.gserviceaccount.com" } KEY ----- \ nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJUV49mSQB2NSO \ nw + tfQWq4pP63U4t7W8V6O6E7FABbYS5N4g35nRzVEj5NciqI27shHSKVrsl7U5ji \ nM0IIA + vi + dgHXwHfCPhS9d85xZ73fuqFaj29iru + pTq9tuNieLDl60L04oCc1qQKBgQDLsMMnX2r9qkQw01H5L2WRB9R6er + bO4DE \ n8Ecpripfd / e7qq89WcGu1H8S + 3Jy49dBmN709vPvVcsGQx8mDdYdm4P0WPkKbgTo \ nt12OA07uiY6Zn54rW5CXrjdoscPXB94AS2ps4M3 / xY5hHTxwtS8yJxoUgTVEfgNB \ nDwzNrZSCVEMCoBAIYl6rWGITgNaR0 + FLuP + kjZw { "type": "service_account", "project_id": "myapp-1086", "private_key_id": "b67c2edcc47c7053c035d8681c8eb7e9f4d90c09", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJUV49mSQB2NSO\nw+tfQWq4pP63U4t7W8V6O6E7FABbYS5N4g35nRzVEj5NciqI27shHSKVrsl7U5ji\nM0IIA+vi+dgHXwHfCPhS9d85xZ73fuqFaj29iru+pTq9tuNieLDl60L04oCc1qQKBgQDLsMMnX2r9qkQw01H5L2WRB9R6er+bO4DE\n8Ecpripfd/e7qq89WcGu1H8S+3Jy49dBmN709vPvVcsGQx8mDdYdm4P0WPkKbgTo\nt12OA07uiY6Zn54rW5CXrjdoscPXB94AS2ps4M3/xY5hHTxwtS8yJxoUgTVEfgNB\nDwzNrZSCVEMCoBAIYl6rWGITgNaR0+FLuP+kjZw\nHsLdkU8173J3nhuYhxo7N90BhO08lquIQ7bJAoGBAKLz\nVRxRdFlcdlMNK34K0dkVh4E4Y8K+9oQWqQeKIrHfWpuSr8CH5q+Dpek8qVGKPnFm\n567XRUzJuLLYzbl2xj1HZWf8KbeTTnALKYg8Jz\nxXXvLlZl2OJ8Frr9ry1DEszPkwWwTQJg5bRG7Z//QfpyEZ2PUvpCNVVpeRuMmUhv\nu5rSLa0G+C97/XIGz/O/1ME9WXU6ZNRwwDkSDw6L7AIrXY8V+8pIRL9e0ks4Uw/A\n6ACYrlYMYYAIl79MNrUrizvF5KwxLiohHJ5KVpThGuRZDaidCPp9BL/h8tfhXPel\nwQot9dM8P4CmQNR/fMpytQSVk7vv95B2JHrt6QmIsQKBgQD9BJ8gfZUVhlxtuaWO\nMKl1PjiD+YKhz4rmIZUKM84xphsGYUBhH29s1zb98u9vlEnlx3bGUtDakNnjTDQp\nagQv22+6STL+0s1haOxyfbi1jIzXvzh47yij6+v7WEIdNj45WV9kpcFTi2oUXURt\nEi3WskYTijYGbDNQmpG2kmY\nDz0KdeTFJxsnFstTT/VozEGvNIHf+8PhKv0123dBFuqSgBD5SFHDp3tQ2IzC81Jm\n5FJLldk3Hw1QRh6+WiEJBTX6nFU4DB5tVXKhbPOvhqYwI/CUYWWbVBQCQgqURcyr\nUdRxAHyDrxKhNrmXXKAEwR0rDz2uGTQCfJ0Zyk/Z1E7iDl/SDfYSSD70wAgGblH2\nAAIQzeoPAgMBAAECggEAb7Trswhft3qmb1V9LEzzN+OtxvHfqqKAkFO4Ijz1+b6R\n3/t4P7KTRhOqaHTZ7zjlu/kbsKzc9casRY+lqybp4/c4jNaGBklG6Vmu96E9wKBgEFPsRe88v/UaAV213Jlw0hdYE9I19yW6z7OSl+Q0dflDbLO5cRs\nTeHlh+9zhzThLVYf79vvwrO4klXm9Mv/7sa/uQ54GK7IkXVklSxUoZpThoOme5hT\n+8ScgJSnyqEpwFQjaslbNBUxtpc9IA2bseP1S7aCVDfZtEp7rHqOFZTvSgol5YD/AoGAdIMNKvg0IiS2xcIEbyHa\nPTNrUfpHeJk/P+Frr1cmDHhGe1l0FWNg9EDlhItAW5EP15ubZPdWQRrV7RuydJlc\ngWesc2RLPIG8+so1TpG1F62+tsQ9lGSF5imiew1x7sQ3H0VIpGtSfTvSMep6fuE7\niuSVbY0UnpxGnqzo9TBAYS4=\n-----END PRIVATE KEY-----\n", "client_email": "localizator@myapp-1086.iam.gserviceaccount.com", "client_id": "704739729071909788554", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/localizator%40myapp-1086.iam.gserviceaccount.com" } \ n567XRUzJuLLYzbl2xj1HZWf8KbeTTnALKYg8Jz \ nxXXvLlZl2OJ8Frr9ry1DEszPkwWwTQJg5bRG7Z // QfpyEZ2PUvpCNVVpeRuMmUhv \ nu5rSLa0G + C97 / XIGz / O / 1ME9WXU6ZNRwwDkSDw6L7AIrXY8V + 8pIRL9e0ks4Uw / A \ n6ACYrlYMYYAIl79MNrUrizvF5KwxLiohHJ5KVpThGuRZDaidCPp9BL / h8tfhXPel \ nwQot9dM8P4CmQNR / fMpytQSVk7vv95B2JHrt6QmIsQKBgQD9BJ8gfZUVhlxtuaWO \ nMKl1PjiD + YKhz4rmIZUKM84xphsGYUBhH29s1zb98u9vlEnlx3bGUtDakNnjTDQp \ nagQv22 + 6STL + 0s1haOxyfbi1jIzXvzh47yij6 + v7WEIdNj45WV9kpcFTi2oUXURt \ { "type": "service_account", "project_id": "myapp-1086", "private_key_id": "b67c2edcc47c7053c035d8681c8eb7e9f4d90c09", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJUV49mSQB2NSO\nw+tfQWq4pP63U4t7W8V6O6E7FABbYS5N4g35nRzVEj5NciqI27shHSKVrsl7U5ji\nM0IIA+vi+dgHXwHfCPhS9d85xZ73fuqFaj29iru+pTq9tuNieLDl60L04oCc1qQKBgQDLsMMnX2r9qkQw01H5L2WRB9R6er+bO4DE\n8Ecpripfd/e7qq89WcGu1H8S+3Jy49dBmN709vPvVcsGQx8mDdYdm4P0WPkKbgTo\nt12OA07uiY6Zn54rW5CXrjdoscPXB94AS2ps4M3/xY5hHTxwtS8yJxoUgTVEfgNB\nDwzNrZSCVEMCoBAIYl6rWGITgNaR0+FLuP+kjZw\nHsLdkU8173J3nhuYhxo7N90BhO08lquIQ7bJAoGBAKLz\nVRxRdFlcdlMNK34K0dkVh4E4Y8K+9oQWqQeKIrHfWpuSr8CH5q+Dpek8qVGKPnFm\n567XRUzJuLLYzbl2xj1HZWf8KbeTTnALKYg8Jz\nxXXvLlZl2OJ8Frr9ry1DEszPkwWwTQJg5bRG7Z//QfpyEZ2PUvpCNVVpeRuMmUhv\nu5rSLa0G+C97/XIGz/O/1ME9WXU6ZNRwwDkSDw6L7AIrXY8V+8pIRL9e0ks4Uw/A\n6ACYrlYMYYAIl79MNrUrizvF5KwxLiohHJ5KVpThGuRZDaidCPp9BL/h8tfhXPel\nwQot9dM8P4CmQNR/fMpytQSVk7vv95B2JHrt6QmIsQKBgQD9BJ8gfZUVhlxtuaWO\nMKl1PjiD+YKhz4rmIZUKM84xphsGYUBhH29s1zb98u9vlEnlx3bGUtDakNnjTDQp\nagQv22+6STL+0s1haOxyfbi1jIzXvzh47yij6+v7WEIdNj45WV9kpcFTi2oUXURt\nEi3WskYTijYGbDNQmpG2kmY\nDz0KdeTFJxsnFstTT/VozEGvNIHf+8PhKv0123dBFuqSgBD5SFHDp3tQ2IzC81Jm\n5FJLldk3Hw1QRh6+WiEJBTX6nFU4DB5tVXKhbPOvhqYwI/CUYWWbVBQCQgqURcyr\nUdRxAHyDrxKhNrmXXKAEwR0rDz2uGTQCfJ0Zyk/Z1E7iDl/SDfYSSD70wAgGblH2\nAAIQzeoPAgMBAAECggEAb7Trswhft3qmb1V9LEzzN+OtxvHfqqKAkFO4Ijz1+b6R\n3/t4P7KTRhOqaHTZ7zjlu/kbsKzc9casRY+lqybp4/c4jNaGBklG6Vmu96E9wKBgEFPsRe88v/UaAV213Jlw0hdYE9I19yW6z7OSl+Q0dflDbLO5cRs\nTeHlh+9zhzThLVYf79vvwrO4klXm9Mv/7sa/uQ54GK7IkXVklSxUoZpThoOme5hT\n+8ScgJSnyqEpwFQjaslbNBUxtpc9IA2bseP1S7aCVDfZtEp7rHqOFZTvSgol5YD/AoGAdIMNKvg0IiS2xcIEbyHa\nPTNrUfpHeJk/P+Frr1cmDHhGe1l0FWNg9EDlhItAW5EP15ubZPdWQRrV7RuydJlc\ngWesc2RLPIG8+so1TpG1F62+tsQ9lGSF5imiew1x7sQ3H0VIpGtSfTvSMep6fuE7\niuSVbY0UnpxGnqzo9TBAYS4=\n-----END PRIVATE KEY-----\n", "client_email": "localizator@myapp-1086.iam.gserviceaccount.com", "client_id": "704739729071909788554", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/localizator%40myapp-1086.iam.gserviceaccount.com" } n5FJLldk3Hw1QRh6 + WiEJBTX6nFU4DB5tVXKhbPOvhqYwI / CUYWWbVBQCQgqURcyr \ nUdRxAHyDrxKhNrmXXKAEwR0rDz2uGTQCfJ0Zyk / Z1E7iDl / SDfYSSD70wAgGblH2 \ nAAIQzeoPAgMBAAECggEAb7Trswhft3qmb1V9LEzzN + OtxvHfqqKAkFO4Ijz1 + b6R \ n3 / t4P7KTRhOqaHTZ7zjlu / kbsKzc9casRY + lqybp4 / c4jNaGBklG6Vmu96E9wKBgEFPsRe88v / UaAV213Jlw0hdYE9I19yW6z7OSl + Q0dflDbLO5cRs \ nTeHlh + 9zhzThLVYf79vvwrO4klXm9Mv / 7sa / uQ54GK7IkXVklSxUoZpThoOme5hT \ n + { "type": "service_account", "project_id": "myapp-1086", "private_key_id": "b67c2edcc47c7053c035d8681c8eb7e9f4d90c09", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJUV49mSQB2NSO\nw+tfQWq4pP63U4t7W8V6O6E7FABbYS5N4g35nRzVEj5NciqI27shHSKVrsl7U5ji\nM0IIA+vi+dgHXwHfCPhS9d85xZ73fuqFaj29iru+pTq9tuNieLDl60L04oCc1qQKBgQDLsMMnX2r9qkQw01H5L2WRB9R6er+bO4DE\n8Ecpripfd/e7qq89WcGu1H8S+3Jy49dBmN709vPvVcsGQx8mDdYdm4P0WPkKbgTo\nt12OA07uiY6Zn54rW5CXrjdoscPXB94AS2ps4M3/xY5hHTxwtS8yJxoUgTVEfgNB\nDwzNrZSCVEMCoBAIYl6rWGITgNaR0+FLuP+kjZw\nHsLdkU8173J3nhuYhxo7N90BhO08lquIQ7bJAoGBAKLz\nVRxRdFlcdlMNK34K0dkVh4E4Y8K+9oQWqQeKIrHfWpuSr8CH5q+Dpek8qVGKPnFm\n567XRUzJuLLYzbl2xj1HZWf8KbeTTnALKYg8Jz\nxXXvLlZl2OJ8Frr9ry1DEszPkwWwTQJg5bRG7Z//QfpyEZ2PUvpCNVVpeRuMmUhv\nu5rSLa0G+C97/XIGz/O/1ME9WXU6ZNRwwDkSDw6L7AIrXY8V+8pIRL9e0ks4Uw/A\n6ACYrlYMYYAIl79MNrUrizvF5KwxLiohHJ5KVpThGuRZDaidCPp9BL/h8tfhXPel\nwQot9dM8P4CmQNR/fMpytQSVk7vv95B2JHrt6QmIsQKBgQD9BJ8gfZUVhlxtuaWO\nMKl1PjiD+YKhz4rmIZUKM84xphsGYUBhH29s1zb98u9vlEnlx3bGUtDakNnjTDQp\nagQv22+6STL+0s1haOxyfbi1jIzXvzh47yij6+v7WEIdNj45WV9kpcFTi2oUXURt\nEi3WskYTijYGbDNQmpG2kmY\nDz0KdeTFJxsnFstTT/VozEGvNIHf+8PhKv0123dBFuqSgBD5SFHDp3tQ2IzC81Jm\n5FJLldk3Hw1QRh6+WiEJBTX6nFU4DB5tVXKhbPOvhqYwI/CUYWWbVBQCQgqURcyr\nUdRxAHyDrxKhNrmXXKAEwR0rDz2uGTQCfJ0Zyk/Z1E7iDl/SDfYSSD70wAgGblH2\nAAIQzeoPAgMBAAECggEAb7Trswhft3qmb1V9LEzzN+OtxvHfqqKAkFO4Ijz1+b6R\n3/t4P7KTRhOqaHTZ7zjlu/kbsKzc9casRY+lqybp4/c4jNaGBklG6Vmu96E9wKBgEFPsRe88v/UaAV213Jlw0hdYE9I19yW6z7OSl+Q0dflDbLO5cRs\nTeHlh+9zhzThLVYf79vvwrO4klXm9Mv/7sa/uQ54GK7IkXVklSxUoZpThoOme5hT\n+8ScgJSnyqEpwFQjaslbNBUxtpc9IA2bseP1S7aCVDfZtEp7rHqOFZTvSgol5YD/AoGAdIMNKvg0IiS2xcIEbyHa\nPTNrUfpHeJk/P+Frr1cmDHhGe1l0FWNg9EDlhItAW5EP15ubZPdWQRrV7RuydJlc\ngWesc2RLPIG8+so1TpG1F62+tsQ9lGSF5imiew1x7sQ3H0VIpGtSfTvSMep6fuE7\niuSVbY0UnpxGnqzo9TBAYS4=\n-----END PRIVATE KEY-----\n", "client_email": "localizator@myapp-1086.iam.gserviceaccount.com", "client_id": "704739729071909788554", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/localizator%40myapp-1086.iam.gserviceaccount.com" } Frr1cmDHhGe1l0FWNg9EDlhItAW5EP15ubZPdWQRrV7RuydJlc \ ngWesc2RLPIG8 + so1TpG1F62 + tsQ9lGSF5imiew1x7sQ3H0VIpGtSfTvSMep6fuE7 \ niuSVbY0UnpxGnqzo9TBAYS4 = \ n ----- END PRIVATE KEY ----- \ n ", { "type": "service_account", "project_id": "myapp-1086", "private_key_id": "b67c2edcc47c7053c035d8681c8eb7e9f4d90c09", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJUV49mSQB2NSO\nw+tfQWq4pP63U4t7W8V6O6E7FABbYS5N4g35nRzVEj5NciqI27shHSKVrsl7U5ji\nM0IIA+vi+dgHXwHfCPhS9d85xZ73fuqFaj29iru+pTq9tuNieLDl60L04oCc1qQKBgQDLsMMnX2r9qkQw01H5L2WRB9R6er+bO4DE\n8Ecpripfd/e7qq89WcGu1H8S+3Jy49dBmN709vPvVcsGQx8mDdYdm4P0WPkKbgTo\nt12OA07uiY6Zn54rW5CXrjdoscPXB94AS2ps4M3/xY5hHTxwtS8yJxoUgTVEfgNB\nDwzNrZSCVEMCoBAIYl6rWGITgNaR0+FLuP+kjZw\nHsLdkU8173J3nhuYhxo7N90BhO08lquIQ7bJAoGBAKLz\nVRxRdFlcdlMNK34K0dkVh4E4Y8K+9oQWqQeKIrHfWpuSr8CH5q+Dpek8qVGKPnFm\n567XRUzJuLLYzbl2xj1HZWf8KbeTTnALKYg8Jz\nxXXvLlZl2OJ8Frr9ry1DEszPkwWwTQJg5bRG7Z//QfpyEZ2PUvpCNVVpeRuMmUhv\nu5rSLa0G+C97/XIGz/O/1ME9WXU6ZNRwwDkSDw6L7AIrXY8V+8pIRL9e0ks4Uw/A\n6ACYrlYMYYAIl79MNrUrizvF5KwxLiohHJ5KVpThGuRZDaidCPp9BL/h8tfhXPel\nwQot9dM8P4CmQNR/fMpytQSVk7vv95B2JHrt6QmIsQKBgQD9BJ8gfZUVhlxtuaWO\nMKl1PjiD+YKhz4rmIZUKM84xphsGYUBhH29s1zb98u9vlEnlx3bGUtDakNnjTDQp\nagQv22+6STL+0s1haOxyfbi1jIzXvzh47yij6+v7WEIdNj45WV9kpcFTi2oUXURt\nEi3WskYTijYGbDNQmpG2kmY\nDz0KdeTFJxsnFstTT/VozEGvNIHf+8PhKv0123dBFuqSgBD5SFHDp3tQ2IzC81Jm\n5FJLldk3Hw1QRh6+WiEJBTX6nFU4DB5tVXKhbPOvhqYwI/CUYWWbVBQCQgqURcyr\nUdRxAHyDrxKhNrmXXKAEwR0rDz2uGTQCfJ0Zyk/Z1E7iDl/SDfYSSD70wAgGblH2\nAAIQzeoPAgMBAAECggEAb7Trswhft3qmb1V9LEzzN+OtxvHfqqKAkFO4Ijz1+b6R\n3/t4P7KTRhOqaHTZ7zjlu/kbsKzc9casRY+lqybp4/c4jNaGBklG6Vmu96E9wKBgEFPsRe88v/UaAV213Jlw0hdYE9I19yW6z7OSl+Q0dflDbLO5cRs\nTeHlh+9zhzThLVYf79vvwrO4klXm9Mv/7sa/uQ54GK7IkXVklSxUoZpThoOme5hT\n+8ScgJSnyqEpwFQjaslbNBUxtpc9IA2bseP1S7aCVDfZtEp7rHqOFZTvSgol5YD/AoGAdIMNKvg0IiS2xcIEbyHa\nPTNrUfpHeJk/P+Frr1cmDHhGe1l0FWNg9EDlhItAW5EP15ubZPdWQRrV7RuydJlc\ngWesc2RLPIG8+so1TpG1F62+tsQ9lGSF5imiew1x7sQ3H0VIpGtSfTvSMep6fuE7\niuSVbY0UnpxGnqzo9TBAYS4=\n-----END PRIVATE KEY-----\n", "client_email": "localizator@myapp-1086.iam.gserviceaccount.com", "client_id": "704739729071909788554", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/localizator%40myapp-1086.iam.gserviceaccount.com" } 

"client_email" is the account ID.

Conclusion


Once again, I note that the utility architecture is designed in such a way that it can be easily expanded or supplemented, the source and the platform operate independently. For example, if you wish, you can add to the list of sources and use as a source a database or any Web service that has an appropriate API for this. So on Google Sheets, the world has not come up with a wedge here, and supporters of the "anti-ganglification" will be able to find a suitable solution for themselves.

I would also like to thank Inna (Foenix) for helping to write the article. Without it, the article would not have been so intelligible and understandable. Inna, thank you for your advice and comments.

On this finish. Thank you for taking the time to get acquainted with my creation. I would be glad to get your opinion, suggestions or criticism in the comments. And do not forget to press the arrows up or down, for me it is very important, I really want to know how Habr community accepted my work.

Links to sources:

→ LocoLaser source code on GitHub
→ Gradle source code on GitHub
→ Example for Android
→ Example for iOS

→ Link to Bintray
→ Example table

Source: https://habr.com/ru/post/323318/


All Articles