R – How to provide internationalization / localization in such a way, that a Property List can be updated any time in the future

internationalizationiphone

I'm looking for a way of how to provide more localizations for my app after it has been released, without the need that Apple has to approve every little change in my localization files. i.e. my app may first ship only in english, but a week later I may add czech, french and spanish as well.

So generally, what would you suggest? As far as I know, for every language there is a subdirectory in Xcode which contains a special strings plist. But that plist is not going to be stored in documents dir, and the standard implementation would always look only at these files on the private directory that can't be changed, right? Is there any practical / easy way to achieve this flexibility or should I just forget it and hard-code my whole app?

Best Answer

I have an application that downloads localized strings from a server. It creates a DownloadedBundle/[lang].lproj directory in application's cache directory and saves the downloaded Localizable.strings file there. Then it creates a NSBundle using the DownloadedBundle directory as the path, and loads the resources from that bundle.

Some code snippets, omitting the actual downloading part:

// Returns the path of the downloaded bundle:
- (NSString*) downloadedBundlePath {
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString* path = [paths objectAtIndex:0];
    return [path stringByAppendingPathComponent:@"DownloadedBundle"];
}

// Returns the downloaded bundle:
- (NSBundle*) downloadedBundle {
    if (downloadedBundle == nil) {
        downloadedBundle = [[NSBundle bundleWithPath:self.downloadedBundlePath] retain];
    }
    return downloadedBundle;
}

// Returns a string from the downloaded bundle:
- (NSString*) localizedStringForKey:(NSString*)key {
    return [self.downloadedBundle localizedStringForKey:key value:nil table:nil];
}

// Saves the downloaded strings file in the bundle:
- (void) saveDownloadedFile:(NSString*)downloadedFilePath inLanguage:(NSString*)language {
    NSFileManager* fileManager = [[NSFileManager alloc] init];
    NSError* error = nil;
    NSString* lprojPath = [[self downloadedBundlePath] 
                           stringByAppendingPathComponent:[language stringByAppendingString:@".lproj"]];
    if (![fileManager createDirectoryAtPath:lprojPath withIntermediateDirectories:YES attributes:nil error:&error]) {
        NSLog(@"Failed to create a directory at %@: %@", lprojPath, error);
        return;
    }

    // Move the downloaded file to be the new strings file:
    NSString* stringsFilePath = [lprojPath stringByAppendingPathComponent:@"Localizable.strings"];
    if ([fileManager fileExistsAtPath:stringsFilePath]) {
        if (![fileManager removeItemAtPath:stringsFilePath error:&error]) {
            NSLog(@"Failed to remove the old strings file at %@: %@", stringsFilePath, error);
            return;
        }
    }
    if (![fileManager moveItemAtPath:downloadedFilePath toPath:stringsFilePath error:&error]) {
        NSLog(@"Failed to move the new strings file from %@ to %@: %@", moveItemAtPath:downloadedFilePath, stringsFilePath, error);
        return;
    }
}

Disclaimer: This code is a simplified version of what I actually have and not tested in this form. But hopefully it gives some idea.

Related Topic