Translation Management

Over the Air (Strings)

Content is machine translated from English by Phrase Language AI.

Update translations for iOS, Android, React Native and Flutter applications with a single click without releasing a new version to the App Store or Google Play. Updates for text labels are instantly pushed to mobile apps.

OTA_user_device_diagram.png

Over-the-Air (OTA) also integrates with the i18Next and Rails platforms, allowing users to update and manage localized content in web applications without requiring a redeployment.

A new release must be created in order for the updated settings to be applied.

When starting an application implementing the iOS, Android, React Native or Flutter SDK for the first time on a device, a unique and random device identifier is generated. This identifier tracks active users over a given period of time. It is not used for any other form or means of tracking and does not contain any user or device information.

The only limit regarding OTA is the amount of MAU (monthly active users), depending on selected pricing plan.

MAU is the number of devices from which translations are requested. Each device is assigned a random ID and the MAU is calculated from the number of different IDs assigned over the last 30 days.

Data

The SDK communicates with the OTA service in order to check for updates and includes the following details with each request:

  • Device identifier (e.g. "F3AFCB10-80A2-84CB-94C0-27F5EF58876D". Unique for this app and therefore does not allow tracking a specific device.)

  • App version (e.g. "1.2.0")

  • Last update of the translation file (e.g. "1542187679")

  • SDK version (e.g. "1.0.0")

  • Locale (e.g. "de-DE")

  • File format (e.g. "strings")

  • Client (e.g. "ios")

  • Distribution ID (ID of the distribution)

  • Environment secret (to distinguish between development from production)

OTA Distributions

Target platforms are defined within the distribution:

  • iOS

  • Android

  • Flutter

  • i18next

  • Rails

Multiple distributions are possible but ideally there is one distribution per project. If using a distribution for iOS and Android, placeholders for the two formats are automatically converted.

Fallbacks

If language fallbacks are set in the language settings of the project the distribution is connected to, strings from the selected language will be displayed if the requested language exists, but the key is not translated.

If a country-specific language (e.g en-GB) is used, but is not part of the release, the system can fall back on a standard version (e.g. en) of that language if it exists in the project. If the language requested is not found at all, the default locale of the project can be served instead.

Create a distribution

To create a distribution, follow these steps:

  1. From the Over the air (OTA) box on the Integrations page, click Configure or the number of configurations if some already exist.

    The Over the air page opens and displays existing configurations.

  2. Click New distribution. The Add distribution windows opens.

  3. In the General tab, provide a Name, which Project the distribution is associated with, required Languages, and required Platforms.

    • For Android distributions, click on the Android tab to select the format option that encloses any translation including HTML tags in CDATA, if required.

    • After selecting the project to associate with the distribution, the Scheduling tab becomes available. If required, use this tab to set up OTA scheduled releases in the distribution.

  4. In the Fallback languages tab, select distribution specific fallback settings as required. Fallback options are prioritized as displayed in the list.

  5. Optionally, click on the Translations tab to select the option to use the latest reviewed version of translations.

  6. Click Save.

    Distribution details are displayed with IDs required by the SDKs. Details can be displayed again by clicking the distribution from the Over the air page.

OTA Releases

To update translations, create a new release within the distribution. The current state of the project is exported and made available to connected clients.

To create a release, follow these steps:

  1. From the Over the air page, click Add release beside the required distribution.

    The New release window opens.

  2. Provide a Description, required Platform, Branch, Locales and App versions.

    If necessary, enter tags to include only keys with specific tags in the release.

  3. Click Save.

    The release is added to the list on the bottom of the distribution details page.

Schedule OTA releases

To set up recurring schedules for releasing the distribution, follow these steps:

  1. From the Over the air page, click on the cog wheel Phrase_Gear.png icon beside the required distribution.

    The Edit distribution window is displayed.

  2. Select the Schedule release tab and click Enable scheduling.

    Release scheduling options are displayed.

    Note

    Scheduling options are also available upon creation of a new distribution.

  3. From the Create releases every dropdown, choose the release frequency by selecting one of the available options:

    • Day

    • Week

      Select the desired weekdays for the scheduled releases.

  4. Provide a time and relevant Time zone.

  5. If necessary, select Branch, Tags and Languages for the scheduled releases.

    • The Branch field is displayed only if branching is enabled in the project. Selecting a branch updates the list of locales and languages.

  6. Optionally, specify the application versions in the Min version and Max version fields.

    Leave blank to apply the schedule release to all versions of the app.

  7. Click Save.

    The distribution is updated with release scheduling information.

Disabling the schedule stops the automatic release of the distribution, but the configured settings are saved.

OTA Mobile SDK and Web Library Reports

Integrating the appropriate mobile SDK or Web library allows updating of translations with a single click, but also provides metrics to measure usage. The mobile SDK and Web library reports give valuable insight into active app users and their app languages. This set of reports can be accessed for each distribution and the data for the reports is updated twice a day.

Reports are provided for number of active users, overall requests, requests per language, requests per platform and for device languages not provided.

Reports for each distribution are accessed via the Reports.jpg icon on the Over the air page.

OTA Android SDK Installation

With the SDK, the app regularly checks for updated translations and downloads them in the background.

Regularly check the latest releases of the Android SDK, especially considering upgrades.

If translations are not being updated:

  • Ensure distribution id and environment secret are correct.

  • Ensure a release was created on for the current app version.

  • Reload the ViewController to make changes appear immediately.

If 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 <major>.<minor>.<point>. format.

Requirements

  • The SDK requires at least appcompat version 1.2.0. If using an older version of appcompat, consider using SDK version 2.1.3

  • The library depends on AndroidX to support backward compatible UI elements such as the toolbar.

Include the SDK

Add a new repository to the root build.gradle:

allprojects {
    repositories {
        ...
        maven { url "https://maven.download.phrase.com" }
    }
}

Add the library as a dependency:

dependencies {
    implementation "com.phrase.android:ota-sdk:3.5.0"
    ...
}

Jetpack Compose support

To enable Jetpack Compose support for OTA translations, follow these steps:

  1. Add the library implementation "com.phrase.android:ota-sdk-compose:3.5.0" to the root build.gradle.

  2. Wrap the Jetpack Compose code in Phrase { ... }.

Configuration

Initialize 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:

public class MainApplication extends Application {
  @Override
  public void onCreate() {
      super.onCreate();
      Phrase.setup(this, "DISTRIBUTION_ID", "ENVIRONMENT_TOKEN");
      Phrase.updateTranslations();
  }

  @Override
  protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(Phrase.wrapApplicationContext(newBase));
  }  
}

Iinject the SDK in each activity, e.g. by creating a base activity which all other activities inherit from:

public class BaseActivity extends AppCompatActivity {
  @NonNull
  @Override
  public AppCompatDelegate getDelegate() {
    return Phrase.getDelegate(this, super.getDelegate());
  }
}

Translations can be used as usual in layouts:

<TextView android:text="@string/translation_key" />

And inside code:

TextView text = (TextView) findViewById(R.id.text_id);
text.setText(R.string.translation_key);

Some 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:

Phrase(contextWrapping = false) {
    Text( text = phraseString(R.string.test) )
}

Configurations for log levels:

  • Java PhraseLog.setLogLevel(Severity.Debug);

  • Kotlin

    PhraseLog.logLevel = Severity.Verbose

  • Other supported logging options

    • None

    • Error

    • Warning

    • Info

    • Debug

    • Verbose

Change language

If not using the system language, a different language can be set in the setLocaleCode method. The language code (locale) must be present in a release.

Phrase.setLocaleCode("fr");
Phrase.updateTranslations();

Custom app version

The 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 use semantic versioning otherwise no translation update is returned. In case the app does not use semantic versioning it is possible to manually override the used app version.

Example:

Phrase.setAppVersion("3.2.4");

The version must be set before calling updateTranslations( ).

Set timeout

The default timeout for translation downloads is set to 10s.

The default can be changed with:

Phrase.setTimeout(10000); // Timeout in milliseconds

Update callback

If the handling of successful translation updates is required, attach a callback handler:

Phrase.updateTranslations(new TranslationsSyncCallback() {
    @Override
    public void onSuccess(boolean translationsChanged) {
    }

    @Override
    public void onFailure() {
    }
});

Translation updates can also be triggered manually. Newly fetched translations are displayed upon the next application launch.

To make the latest translations immediately available, use the method Phrase.applyPendingUpdates(). This can be combined with listening for translation updates:

Phrase.updateTranslations(new TranslationsSyncCallback() {
    @Override
    public void onSuccess(boolean translationsChanged) {
      if(translationsChanged) {
         Phrase.applyPendingUpdates()
        // Custom logic to refresh UI
      }
    }

    @Override
    public void onFailure() {
    }
});

The UI does not display translations automatically and must be recreated.

Configure US data center

Phrase US data center is also supported. The US data center can be configured by calling:

Phrase.setHost("https://ota.us.app.phrase.com")

Fallback

In 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.

The 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.

Add a new language

Creating 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.

Auditing

The 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.

Custom View Support

Custom views can be translated using styled attributes. Since TypedArray does not allow overwriting the resources, slight changes in the custom view are required:

  • Kotlin example

    Before:

    context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0).use {
        text = it.getText(R.styleable.CustomView_android_text)
    }

    After:

    context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0).use {
        text = it.getTextWithPhrase(R.styleable.CustomView_android_text)
    }
  • Java example

    Before:

    final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);
    try {
        setText(ta.getText(R.styleable.CustomView_android_text));
    } finally {
        ta.recycle();
    }

    After:

    final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);
    try {
        setText(PhraseTypedArray.getTextWithPhrase(ta, R.styleable.CustomView_android_text));
    } finally {
        ta.recycle();
    }

Example app

https://github.com/phrase/android-sdk-example

OTA Flutter SDK Installation

With the SDK, the app regularly checks for updated translations and downloads them in the background.

Requirements

This library depends on 0.17.0 version of Flutter's intl library.Follow their guide to add localizations support to the app.

Installation

Add Phrase to the pubspec.yaml:

dependencies:
  phrase: ^1.0.1 
  ...
  intl: ^0.17.0
  flutter_localizations:
    sdk: flutter
  ...

flutter:
  generate: true
  ...

Like in the intl library, code generation is used to process ARB files. Run this command to update:

flutter pub run phrase

Using build_runner:

flutter pub run build_runner watch

Usage

Initialize Phrase in the main.dart file:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_gen/gen_l10n/phrase_localizations.dart';
import 'package:phrase/phrase.dart';

void main() {
  Phrase.setup("[DISTRIBUTION_ID]", "[ENVIRONMENT_ID]");
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      //..
      localizationsDelegates: PhraseLocalizations.localizationsDelegates,
      supportedLocales: PhraseLocalizations.supportedLocales,
    );
  }
}

Access messages with:

Text(AppLocalizations.of(context)!.helloWorld);

Customization

Update behavior

OTA translations are updated every time the app launches. To disable this:

Phrase.setup("[DISTRIBUTION_ID]", "[ENVIRONMENT_ID]", 
    checkForUpdates: false);

To update manually:

Phrase.updateTranslations(context).then((_) => print("Done!"));

Custom app version

The 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 use semantic versioning otherwise no translation update will be returned. In case app does not use semantic versioning, the app version can be manually overridden: it is possible to manually override the app version:

Phrase.setup("[DISTRIBUTION_ID]", "[ENVIRONMENT_ID]",
    customAppVersion: "1.2.3");

Configure US data center

Phrase US data center is also supported. The US data center can be selected by passing the relevant API hostname parameter in the SDK configuration:

Phrase.setup("[DISTRIBUTION_ID]", "[ENVIRONMENT_ID]", host: PhraseHost.us);

Example app

https://github.com/phrase/flutter_sdk_example

OTA iOS Installation

With the SDK, the app regularly checks for updated translations and downloads them in the background.

Mac Catalyst is also supported.

The SDK can be installed manually or via Swift Package Manager, Carthage or Cocoa Pods.

If translations are not being updated:

  • Ensure distribution id and environment secret are correct.

  • Ensure a release was created on for the current app version.

  • Reload the ViewController to make changes appear immediately.

If the wrong version of a translation is being used, ensure a release with the latest translations and the current app version is available.

Swift Package Manager

Add the public repository URL (https://github.com/phrase/ios-sdk/). Xcode automatically handles the rest of the installation.

Carthage

Add the following line into your Cartfile:

binary "https://raw.githubusercontent.com/phrase/ios-sdk/master/PhraseSDK.json" ~> 3.0.0

Run carthage update and add the PhraseApp.framework  to your project as desribed in the Carthage documentation.

Cocoa Pods

Add the following line into your Podfile:

pod 'PhraseSDK'

Run pod install. If new to CocoaPods, see their documentation.

Manual installation

Follow these steps:

  1. Download the latest release.

  2. Add PhraseSDK.framework in Xcode as the linked binary to the target.

  3. A script to strip the extra binaries needs to be run before you upload the app as the Apple store rejects apps including simulator binaries.

    Go to Build Phases and add a Run Script section by clicking the + symbol. Paste in this script:

    FRAMEWORK="PhraseSDK"
    FRAMEWORK_EXECUTABLE_PATH="${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/$FRAMEWORK.framework/$FRAMEWORK"
    EXTRACTED_ARCHS=()
    for ARCH in $ARCHS
    do
       lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
       EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
    done
    lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
    rm "${EXTRACTED_ARCHS[@]}"
    rm "$FRAMEWORK_EXECUTABLE_PATH"
    mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"

Configuration

  1. Import PhraseSDK:

    import PhraseSDK
  2. Initialize the SDK by calling the following code:

    Phrase.shared.setup(
      distributionID: <Distribution ID>, 
      environmentSecret: <Environment Secret>
    )
  3. To update localization files call Phrase.shared.updateTranslation().

    This method will raises an exception if SDK is not correctly setup.

    To configure OTA to use the US data center, set the host before calling PhraseApp.shared.updateTranslation() with Phrase.shared.configuration.apiHost = .us.

Calling both functions within the AppDelegate in the applicationDidFinishLaunchingWithOptions method is recommended.

Objective-C

Integrate the SDK into the Objective-C application:

@import PhraseSDK;

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [[Phrase shared] setDebugMode:true]; // Optional

  [[Phrase shared] setupWithDistributionID:@"Your Distribution ID"
                         environmentSecret:@"Your Environment Secret"];

  // OR:
  //
  //  [[Phrase shared] setupWithDistributionID:@"Your Distribution ID"
  //                         environmentSecret:@"Your Environment Secret"
  //                                   timeout:10];

  // Update translations using callback block:
  [[Phrase shared] updateTranslationsWithCompletionHandler:^(BOOL updated, NSError* error){
    NSLog(@"Updated: %@", updated ? @"true" : @"false");

    if (error) {
      NSLog(@"Domain: %@ Code: %ld Message: %@", error.domain, (long)error.code, error.localizedDescription);
    } else {
      NSLog(@"No error");
    }

    // Translate via bundle proxy:
    NSString *translation = NSLocalizedString(@"layouts.application.about", @"");
    NSLog(@"NSLocalizedString via bundle proxy: %@", translation);

    // OR:
    //
    // Translate using fallback method:
    NSString *otherTranslation = [[Phrase shared]
                                  localizedStringForKey:@"layouts.application.about" value:NULL table:NULL];
    NSLog(@"Phrase.shared localizedStringForKey: %@", otherTranslation);
  }];

  // OR:
  //
  // [[Phrase shared] updateTranslationsWithCompletionHandler:NULL]; // ignore result and errors (not recommended)

  // [...] Your other code
  
  return YES;
}

Disable swizzling

To disable swizzling, set PhraseSDKMainBundleProxyDisabled to YES in the Info.plist file.

When swizzling is disabled, updated translations are no longer be displayed. The translation will still be synced if updateTranslation is called and can be accessed with the Phrase.localizedString() method.

App version handling

To determine which release should be returned the SDK requires a semantic version of the app so translations are updated.

The SDK attempts to get a semantic version the following way:

  • CFBundleShortVersionString is used if semantic.

  • If not, CFBundleVersion is used if semantic.

  • If both are not semantic, a combination of (CFBundleShortVersionString.CFBundleVersion) is used.

If CFBundleShortVersionString is missing or unable to be created with a semantic version together with CFBundleVersion, the SDK throws the PhraseSetupError.appVersionNotSemantic message.

Disable translation for multiple tables

To prevent OTA from translating any tables in the main iOS bundle other than the default Localizable table, set the following option:

Phrase.shared.configuration.ignoreOtherTables = true

Callbacks

Attach a callback handler to handle successful translation updates:

Phrase.shared.updateTranslation { result in
            switch result {
            case .success(let updated):
            case .failure:
            }
        }

Debug mode

If further information is required, enable the debug mode to get additional logging of the PhraseSDK.framework into the console:

Phrase.shared.configuration.debugMode = true

Set timeout for requests

Set a timeout for the requests against Phrase by calling:

Phrase.shared.configuration.timeout = TimeInterval(20)

The default timeout is 10 seconds and connections taking longer than 10 seconds will be closed.

Provide manual language override

If not using the system language as the locale, a different locale can be set in the init call. The locale code needs to be present in a release from Phrase:

Phrase.shared.configuration.localeOverride = "en-US"

Fallback

In case new translations cannot be fetched from Phrase via the SDK, the latest translation files that the installation received are used. If the App never received new files from Phrase, it uses the compiled translation files of app. This prevents errors in case of any technical difficulties or networking errors. Keeping your translation files that are compiled into the app up to date with every release is recommended.

Auditing

The 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.

OTA React Native SDK Installation

With the SDK, the app regularly checks for updated translations and downloads them in the background.

The library for OTA translations only works with the react-i18next library.

To install the React Native SDK, follow these steps:

  1. Run this command:

    $ npm install react-native-phrase-sdk --save
  2. Initialize Phrase:

    import Phrase from "react-native-phrase-sdk";
    
    let phrase = new Phrase(
        "YOUR_DISTRIBUTION_ID",
        "YOUR_DEVELOPMENT_OR_PRODUCTION_SECRET",
        "YOUR_APP_VERSION",
        "i18next"
    );

    The file format can be either i18next (as in the example above) or i18next_4, which results in the i18next v4 format file.

    Phrase US data center is also supported. To use the React Native SDK with the US data center, pass the relevant host during initialization:

    import Phrase from "react-native-phrase-sdk";
    
    let phrase = new Phrase(
        "YOUR_DISTRIBUTION_ID",
        "YOUR_DEVELOPMENT_OR_PRODUCTION_SECRET",
        "YOUR_APP_VERSION",
        "i18next",
        host="https://ota.us.app.phrase.com"
    );  
    
  3. Create i18next backend based on instance:

    import resourcesToBackend from "i18next-resources-to-backend";
    
    const backendPhrase = resourcesToBackend((language, namespace, callback) => {
        phrase.requestTranslation(language)
            .then((remoteResources) => {
                callback(null, remoteResources);
            })
            .catch((error) => {
                callback(error, null);
            });
    });
    
    const backendFallback = resourcesToBackend(localResources)
  4. Initialize i18n with Phrase backend:

    i18n
      .use(ChainedBackend)
      .use(initReactI18next)
      .init({
        backend: {
            backends: [backendPhrase, backendFallback]
        }
        //...
      });

Sample i18next.js file

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import ChainedBackend from "i18next-chained-backend";
import resourcesToBackend from "i18next-resources-to-backend";
import translationEN from "./locales/en/translation.json";
import translationRU from "./locales/ru/translation.json";
import Phrase from "react-native-phrase-sdk";

const localResources = {
  en: {
    translation: translationEN,
  },
  ru: {
    translation: translationRU,
  },
};

let phrase = new Phrase(
    "YOUR_DISTRIBUTION_ID",
    "YOUR_ENVIRONMENT_ID",
    require('./package.json').version,
    "i18next"
);

const backendPhrase = resourcesToBackend((language, namespace, callback) => {
    phrase.requestTranslation(language)
        .then((remoteResources) => {
            callback(null, remoteResources);
        })
        .catch((error) => {
            callback(error, null);
        });
});

const backendFallback = resourcesToBackend(localResources)

i18n
  .use(ChainedBackend)
  .use(initReactI18next)
  .init({
    backend: {
        backends: [backendPhrase, backendFallback]
    },
    debug: true,
    lng: "en",
    fallbackLng: "en",
    interpolation: {
      escapeValue: false, // not needed for react as it escapes by default
    }
  });

Example app

https://github.com/phrase/react_native_sdk_example

OTA i18next Backend Installation

The i18next-phrase-backend library implements an example backend for i18next which retrieves the translations from OTA releases.

To create a new release, the i18next platform needs to be set for the distribution and be part of a release.

Usage

To initialize Phrase:

import i18n from "i18next";
import { I18nextPhraseBackend } from "@phrase/i18next-backend";

i18n
  .use(I18nextPhraseBackend)
  .init({
    fallbackLng: 'en',
    backend: {
      distribution: 'DISTRIBUTION_ID',
      secret: 'YOUR_ENVIRONMENT_SECRET',
      appVersion: '1.0.0',
    }
  });

Caching

The library is caching translations and will not check for new translations for 5 minutes. Caching can be configured by setting the cacheExpirationTime option in the backend configuration for testing purposes.

i18n
  .use(I18nextPhraseBackend)
  .init({
    fallbackLng: 'en',
    backend: {
      distribution: 'DISTRIBUTION_ID',
      secret: 'YOUR_ENVIRONMENT_SECRET',
      appVersion: '1.0.0',
      cacheExpirationTime: 60 * 5, // time in seconds
    }
  });

Note

It is recommended to use at least 5 minutes caching time in production environments.

Example app

https://github.com/phrase/ota-web-demo

OTA Rails Backend Installation

The phrase-ota-i18n library implements an example backend for the I18n gem which retrieves the translations from OTA releases. With the library, the application regularly checks for updated translations and downloads them in the background.

The library is valid for any Ruby application using the i18n gem.

To create a new release, the Rails platform needs to be set for the distribution and be part of a release.

Installation

  1. Add the following line to the application's Gemfile:

    gem 'phrase-ota-i18n'
  2. Execute:

    $ bundle install
  3. Alternatively, install it as:

    $ gem install phrase-ota-i18n

Usage

To generate the configuration:

bundle exec rails generate phrase_ota:install --distribution-id <DISTRIBUTION_ID> --secret-token <SECRET>

Debugging

The gem has a debug configuration which can be enabled in the initializer by setting:

config.debug = true
Was this article helpful?

Sorry about that! In what way was it not helpful?

The article didn’t address my problem.
I couldn’t understand the article.
The feature doesn’t do what I need.
Other reason.

Note that feedback is provided anonymously so we aren't able to reply to questions.
If you'd like to ask a question, submit a request to our Support team.
Thank you for your feedback.