{"id":22831536,"url":"https://github.com/phrase/phrase-android","last_synced_at":"2025-08-09T07:19:01.678Z","repository":{"id":36966117,"uuid":"165238560","full_name":"phrase/phrase-android","owner":"phrase","description":"Phrase Over the Air Android SDK","archived":false,"fork":false,"pushed_at":"2025-02-05T14:08:57.000Z","size":14,"stargazers_count":6,"open_issues_count":9,"forks_count":1,"subscribers_count":20,"default_branch":"main","last_synced_at":"2025-03-30T02:51:08.562Z","etag":null,"topics":["android","i18n","localization","ota","over-the-air","phraseapp","translation"],"latest_commit_sha":null,"homepage":"https://phrase.com","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phrase.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-11T12:14:44.000Z","updated_at":"2025-02-05T14:09:02.000Z","dependencies_parsed_at":"2024-12-12T20:34:00.680Z","dependency_job_id":"fbb16986-6448-42f8-8b86-aecf62b71b9e","html_url":"https://github.com/phrase/phrase-android","commit_stats":null,"previous_names":[],"tags_count":65,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phrase%2Fphrase-android","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phrase%2Fphrase-android/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phrase%2Fphrase-android/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phrase%2Fphrase-android/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phrase","download_url":"https://codeload.github.com/phrase/phrase-android/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250501403,"owners_count":21441021,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","i18n","localization","ota","over-the-air","phraseapp","translation"],"created_at":"2024-12-12T20:26:31.282Z","updated_at":"2025-08-09T07:19:01.270Z","avatar_url":"https://github.com/phrase.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Phrase Over the Air SDK for Android\n\nPublish your translations faster and simpler than ever before. Stop waiting for the next deployment and start publishing all your translations in real-time directly in Phrase.\n\nFor more details on how OTA works, visit the Phrase Help Center: https://support.phrase.com/hc/en-us/articles/5804059067804-Over-the-Air-Strings\n\n## Instructions\n\nWith the SDK, the app will regularly check for updated translations and downloads them in the background. Translations are fetched when `updateTranslations` is called, which should usually happen in `onCreate`.\n\n\n### Requirements\n- The SDK requires at least appcompat version 1.2.0. If using an older version of appcompat, consider using SDK version [2.1.3](https://github.com/phrase/phrase-android/releases/tag/2.1.3)\n- The library depends on AndroidX to support backward compatible UI elements such as the toolbar\n\n### Include the SDK\nAdd a new repository to the root `build.gradle`:\n\n```\nallprojects {\n    repositories {\n        ...\n        maven { url \"https://maven.download.phrase.com\" }\n    }\n}\n```\n\nAdd the library as a dependency:\n\n```\ndependencies {\n    implementation \"com.phrase.android:ota-sdk:3.10.2\"\n    ...\n}\n```\n\n### Jetpack Compose Support\nTo enable Jetpack Compose support for OTA translations, follow these steps:\n1. Add the library `implementation \"com.phrase.android:ota-sdk-compose:3.10.2\"` to the root build.gradle.\n2. Wrap the Jetpack Compose code in `Phrase { ... }`.\n\n### Configuration\nInitialize the SDK in the application class and add the distribution ID and environment secret. Classes inheriting from Application should overwrite `attachBaseContext` to enable translations outside of the activity context:\n\n```java\npublic class MainApplication extends Application {\n  @Override\n  public void onCreate() {\n      super.onCreate();\n      Phrase.setup(this, \"DISTRIBUTION_ID\", \"ENVIRONMENT_TOKEN\");\n      Phrase.updateTranslations();\n  }\n\n  @Override\n  protected void attachBaseContext(Context newBase) {\n    super.attachBaseContext(Phrase.wrapApplicationContext(newBase));\n  }\n}\n```\n\nInject the SDK in each activity, e.g. by creating a base activity which all other activities inherit from:\n\n```java\npublic class BaseActivity extends AppCompatActivity {\n  @NonNull\n  @Override\n  public AppCompatDelegate getDelegate() {\n    return Phrase.getDelegate(this, super.getDelegate());\n  }\n}\n```\n\nTranslations can be used as usual in layouts:\n```xml\n\u003cTextView android:text=\"@string/translation_key\" /\u003e\n```\nAnd inside code:\n```java\nTextView text = (TextView) findViewById(R.id.text_id);\ntext.setText(R.string.translation_key);\n```\n\nSome libraries do not support automatically unwrapping the context and expect a specific class. In this case context wrapping in Jetpack Compose components can be disabled with:\n\n```\nPhrase(contextWrapping = false) {\n    Text( text = phraseString(R.string.test) )\n}\n```\n\n### Configurations for log levels:\n\nJava\n```java\nPhraseLog.setLogLevel(Severity.Debug);\n```\n\nKotlin\n```kotlin\nPhraseLog.logLevel = Severity.Verbose\n```\n\nOther supported logging values: `None`, `Error`, `Warning`, `Info`, `Debug`, `Verbose`\n\n### Custom app version\nThe SDK uses the app version by default to return a release which matches the release constraints for the min and max version. The app version must follow semantic versioning. Otherwise, no translations updates will be returned. In case the app does not use semantic versioning it is possible to manually override the used app version.\n\nExample:\n`Phrase.setAppVersion(\"3.2.4\");`\nThe version must be set before calling `updateTranslations()`.\n\n### Set timeout\nThe default timeout for translation downloads is set to 10s. The default can be changed with:\n```\n// Timeout in milliseconds\nPhrase.setTimeout(10000);\n```\n\n### Update callback\nIf the handling of successful translation updates is required, attach a callback handler:\n\n```java\nPhrase.updateTranslations(new TranslationsSyncCallback() {\n    @Override\n    public void onSuccess(boolean translationsChanged) {\n    }\n\n    @Override\n    public void onFailure() {\n    }\n});\n```\n\nTranslation updates can also be triggered manually. Newly fetched translations are displayed upon the next application launch.\n\nTo make the latest translations immediately available, use the method `Phrase.applyPendingUpdate()`. This can be combined with listening for translation updates:\n\n```java\nPhrase.updateTranslations(new TranslationsSyncCallback() {\n    @Override\n    public void onSuccess(boolean translationsChanged) {\n      if(translationsChanged) {\n         Phrase.applyPendingUpdate()\n        // Custom logic to refresh UI\n      }\n    }\n\n    @Override\n    public void onFailure() {\n    }\n});\n```\n\nThe UI does not display translations automatically and must be recreated.\n\n### Configure US data center\nPhrase US data center is also supported. The US data center can be configured by calling:\n\n```\nPhrase.setDatacenter(PhraseDataCenter.US)\n```\n\n### Fallback\nIn case it is not possible to reach Phrase due to a missing network connection of the client or a service interruption, the SDK uses the bundled translations from the resource file. The regular updating of the bundled translations in the app is recommended. The SDK also caches translations locally on the device. If such a cache exists, it is used until the next translation update.\n\nThe SDK uses the most recent release for the translations. In case the versionName for the app is set, the most recent release that satisfies the version restrictions will be used.\n\n### Add a new language\nCreating the new language in Phrase and create a new release. The SDK fetches the language when this is the device language of a user. Regularly adding a new strings.xml for new languages files when releasing a new app version is recommended or users will only see the fallback translations determined by Android at the first start of the app.\n\n### Auditing\nThe SDK is closed source and can not be viewed or modified. If it is an organization requirement, audits can be provided. Contact us for more details if required.\n\n### Custom View Support\nCustom views can be translated using styled attributes. Since TypedArray does not allow overwriting the resources, slight changes in the custom view are required:\n\n### Kotlin example\n\nBefore:\n\n```kotlin\ncontext.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0).use {\n    text = it.getText(R.styleable.CustomView_android_text)\n}\n```\nAfter:\n\n```kotlin\ncontext.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0).use {\n    text = it.getTextWithPhrase(R.styleable.CustomView_android_text)\n}\n```\n\n### Java example\n\nBefore:\n\n```java\nfinal TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);\ntry {\n    setText(ta.getText(R.styleable.CustomView_android_text));\n} finally {\n    ta.recycle();\n}\n```\nAfter:\n\n```java\nfinal TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);\ntry {\n    setText(PhraseTypedArray.getTextWithPhrase(ta, R.styleable.CustomView_android_text));\n} finally {\n    ta.recycle();\n}\n```\n\nExample [app](https://github.com/phrase/android-sdk-example)\n\n### Troubleshooting\n**If translations are not being updated**\n- Ensure distribution id and environment secret are correct.\n- Ensure a release was created on for the current app version.\n- Reload the ViewController to make changes appear immediately.\n\nIf the wrong version of a translation is being used, ensure a release with the latest translations and the current app version is available and the `versionName` for the app set and are using the `\u003cmajor\u003e.\u003cminor\u003e.\u003cpoint\u003e`. format.\n\n\n## Limitations\n\n- Inflated menus and preferences are not supported yet\n- Texts in custom views need to be updated [manually](https://github.com/phrase/phrase-android/?tab=readme-ov-file#custom-view-support)\n- Kotlin Multiplatform is not officially supported\n- Supported Android versions: API level 21+\n\n### Jetpack Compose Limits\n- Views need to be wrapped with `Phrase { ... }`\n- Plural translations are not yet supported with Compose\n- Some libraries do not support automatic context unwrapping. For example `Hilt` requires to disable automatic context wrapping in Compose components. More details can be found [here](https://github.com/phrase/phrase-android/?tab=readme-ov-file#configuration)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphrase%2Fphrase-android","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphrase%2Fphrase-android","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphrase%2Fphrase-android/lists"}