Localization
Working with localized strings and resources.
Resolving Localized Labels
typescript
import { Apk } from "node-apk";
const apk = new Apk("app.apk");
// Get default label
const defaultLabel = await apk.getLabel();
console.log(`Default: ${defaultLabel}`);
// Get specific locale
const frenchLabel = await apk.getLabel({ locale: "fr" });
console.log(`French: ${frenchLabel}`);
const germanLabel = await apk.getLabel({ locale: "de" });
console.log(`German: ${germanLabel}`);Manual Resource Resolution
For more control over localization:
typescript
import { Apk } from "node-apk";
const apk = new Apk("app.apk");
const manifest = await apk.getManifestInfo();
const resources = await apk.getResources();
const labelId = manifest.applicationLabel;
if (typeof labelId === "number") {
const resolved = resources.resolve(labelId);
for (const res of resolved) {
const lang = res.locale?.language ?? "default";
const country = res.locale?.country ?? "";
const locale = country ? `${lang}-${country}` : lang;
console.log(`${locale}: ${res.value}`);
}
}Building a Locale Map
typescript
function buildLocaleMap(
resources: Resources,
id: number
): Map<string, string> {
const resolved = resources.resolve(id);
const map = new Map<string, string>();
for (const res of resolved) {
if (typeof res.value === "string") {
// Use language code as key
const key = res.locale?.language ?? "default";
map.set(key, res.value);
}
}
return map;
}
// Usage
const apk = new Apk("app.apk");
const manifest = await apk.getManifestInfo();
const resources = await apk.getResources();
const labels = buildLocaleMap(resources, manifest.applicationLabel as number);
console.log("Available translations:");
for (const [locale, label] of labels) {
console.log(` ${locale}: ${label}`);
}Fallback Chain
Implement a localization fallback strategy:
typescript
function getLocalizedValue(
resources: Resources,
id: number,
preferredLocales: string[]
): string | undefined {
const resolved = resources.resolve(id);
// Try each preferred locale in order
for (const locale of preferredLocales) {
// Try exact match (e.g., "en-US")
const exact = resolved.find(r => {
const lang = r.locale?.language;
const country = r.locale?.country;
const parts = locale.split("-");
return lang === parts[0] && country === parts[1];
});
if (exact?.value) return String(exact.value);
// Try language match (e.g., "en")
const langMatch = resolved.find(r =>
r.locale?.language === locale.split("-")[0]
);
if (langMatch?.value) return String(langMatch.value);
}
// Fallback to default (no locale)
const default_ = resolved.find(r => !r.locale);
if (default_?.value) return String(default_.value);
// Last resort: first available
return resolved[0]?.value ? String(resolved[0].value) : undefined;
}
// Usage
const label = getLocalizedValue(resources, labelId, [
"fr-FR", // Try French France first
"fr", // Then any French
"en-US", // Then English US
"en", // Then any English
]);Extracting All Localized Strings
typescript
import { Apk } from "node-apk";
import { promises as fs } from "node:fs";
async function extractAllStrings(apkPath: string, outputDir: string) {
const apk = new Apk(apkPath);
const manifest = await apk.getManifestInfo();
const resources = await apk.getResources();
// Get label translations
const labelId = manifest.applicationLabel;
if (typeof labelId === "number") {
const resolved = resources.resolve(labelId);
for (const res of resolved) {
if (typeof res.value === "string") {
const lang = res.locale?.language ?? "default";
const filename = `${outputDir}/strings-${lang}.json`;
// Read existing or create new
let strings: Record<string, string> = {};
try {
strings = JSON.parse(await fs.readFile(filename, "utf8"));
} catch {}
strings.app_name = res.value;
await fs.writeFile(filename, JSON.stringify(strings, null, 2));
}
}
}
}Locale Detection from Accept-Language
typescript
function parseAcceptLanguage(header: string): string[] {
return header
.split(",")
.map(part => {
const [lang, q = "1"] = part.trim().split(";q=");
return { lang: lang.trim(), q: parseFloat(q) };
})
.sort((a, b) => b.q - a.q)
.map(item => item.lang);
}
// Usage in a web server
function getAppNameForRequest(apk: Apk, acceptLanguage: string) {
const locales = parseAcceptLanguage(acceptLanguage);
return apk.getLabel({ locale: locales[0] });
}
// Example
const locales = parseAcceptLanguage("fr-FR;q=0.9, en;q=0.8, de;q=0.7");
console.log(locales); // ["fr-FR", "en", "de"]Related
- Resources API - Resource resolution
- API: getLabel - Label extraction